Discussion:
port_getn() and timeouts - is this a bug or an undocumented feature?
Jeff Trawick
2009-08-19 19:59:51 UTC
Permalink
If an event is triggered at about the same time as a timeout expires,
the port_getn() libc interface can return with

retcode: -1
errno: ETIME
number of events: updated to > 0

The -1/ETIME must be ignored and the event(s) processed, as the kernel
will not report it again until after a subsequent call to port_associate().

I've seen it on 2009.06 and S10U7.

The man page has two descriptions for ETIME:

The port_get() and port_getn() functions will fail if:
...
ETIME The time interval expired before the expected
number of events have been posted to the port.
...
The port_getn() function will fail if:
...
ETIME The time interval expired before the expected
number of events have been posted to the port
(original value in nget), or nget is updated with
the number of returned port_event_t structures in
list[].

The second description of ETIME is perhaps unclear (is it "before A or B
occurs" or "before A occurs; otherwise"); regardless, neither
description mentions this behavior.

I couldn't reproduce this behavior with port_get().

libevent handles/works around this behavior as follows (see the "errno
== ETIME" path):

if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
&nevents, &ts)) == -1) {
if (errno == EINTR) {
evsignal_process();
return (0);
} else if (errno == ETIME) {
if (nevents == 0)
return (0);
} else {
perror("port_getn");
return (-1);
}

This newbie finds the special ETIME check in port_getn() at
http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/event_port.c
surprising, but then of course I would since ETIME is the heart of the
problem.

Can anyone advise on how an app can avoid pitfalls in this area
regardless of *Solaris level?

I will open a bug at bugs.opensolaris.org to fix the code or the doc,
unless for some reason shared here it isn't needed or there is a better
way to resolve the issue.

Thanks!
Nicolas Williams
2009-08-19 20:52:09 UTC
Permalink
Post by Jeff Trawick
If an event is triggered at about the same time as a timeout expires,
the port_getn() libc interface can return with
retcode: -1
errno: ETIME
number of events: updated to > 0
Sounds like the desired behavior.
Post by Jeff Trawick
The -1/ETIME must be ignored and the event(s) processed, as the kernel
will not report it again until after a subsequent call to port_associate().
Sounds right.
Post by Jeff Trawick
I've seen it on 2009.06 and S10U7.
...
ETIME The time interval expired before the expected
number of events have been posted to the port.
...
...
ETIME The time interval expired before the expected
number of events have been posted to the port
(original value in nget), or nget is updated with
the number of returned port_event_t structures in
list[].
The second description of ETIME is perhaps unclear (is it "before A or B
occurs" or "before A occurs; otherwise"); regardless, neither
description mentions this behavior.
The wording in the manpage text that you quote is confusing, wrong even.
That ", or" should be " and" or ". ".

ETIME is returned if the timer expires. Regardless of whether ETIME
happens, *nget gets updated to the number of events returned (if neither ETIME
nor EINTR occur, and *nget <= max, then *nget will not be modified, of
course).

Think of ETIME/EINTR not as an error but as a supplementary bit of
inforation: port_getn() always returns, via the nget input/output
argument, the number of events fetched, and ETIME/EINTR merely indicate
whether a timeout or signal interrupted the wait for more events.

A timeout or signal could occur before _any_ events occur, in which case
*nget will == 0.

Imagine an alternative port_getn() design where nget is not a pointer
and port_getn() returns the number of arguments fetched. Ah, but to
avoid confusion you'd always have to first set errno = 0 _before calling
port_getn()_, and that'd be obnoxious. Thus (I conjecture) the actual
design of port_getn().
Post by Jeff Trawick
I couldn't reproduce this behavior with port_get().
libevent handles/works around this behavior as follows (see the "errno
if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
&nevents, &ts)) == -1) {
if (errno == EINTR) {
evsignal_process();
return (0);
} else if (errno == ETIME) {
if (nevents == 0)
return (0);
This is correct, not a "workaround". The returned events must get
processed.
Post by Jeff Trawick
} else {
perror("port_getn");
return (-1);
}
[...]
Can anyone advise on how an app can avoid pitfalls in this area
regardless of *Solaris level?
Just like libevent does it.
Post by Jeff Trawick
I will open a bug at bugs.opensolaris.org to fix the code or the doc,
unless for some reason shared here it isn't needed or there is a better
way to resolve the issue.
That'd be great, thanks.

Nico
--
Jeff Trawick
2009-08-19 21:19:40 UTC
Permalink
Post by Nicolas Williams
Post by Jeff Trawick
If an event is triggered at about the same time as a timeout expires,
the port_getn() libc interface can return with
retcode: -1
errno: ETIME
number of events: updated to > 0
Sounds like the desired behavior.
Post by Jeff Trawick
The -1/ETIME must be ignored and the event(s) processed, as the kernel
will not report it again until after a subsequent call to port_associate().
Sounds right.
First, thanks!

A lot of other people can't code correctly to this type of interface.
Even Sun examples can be wrong
(http://developers.sun.com/solaris/articles/listing2_poll.c).
Post by Nicolas Williams
Post by Jeff Trawick
I've seen it on 2009.06 and S10U7.
...
ETIME The time interval expired before the expected
number of events have been posted to the port.
...
...
ETIME The time interval expired before the expected
number of events have been posted to the port
(original value in nget), or nget is updated with
the number of returned port_event_t structures in
list[].
The second description of ETIME is perhaps unclear (is it "before A or B
occurs" or "before A occurs; otherwise"); regardless, neither
description mentions this behavior.
The wording in the manpage text that you quote is confusing, wrong even.
That ", or" should be " and" or ". ".
ETIME is returned if the timer expires. Regardless of whether ETIME
happens, *nget gets updated to the number of events returned (if neither ETIME
nor EINTR occur, and *nget <= max, then *nget will not be modified, of
course).
Think of ETIME/EINTR not as an error but as a supplementary bit of
inforation: port_getn() always returns, via the nget input/output
argument, the number of events fetched, and ETIME/EINTR merely indicate
whether a timeout or signal interrupted the wait for more events.
Can you confirm that ETIME and EINTR are the only two -1/errno
indications that can be reported at the same time that one or more
events is returned?

Alternatively, can you state categorically that if an event is returned,
no errors (EBADF, EBADFD, EFAULT, etc.) occurred?

I'd like to fix my code and not worry about some other errno having a
similar issue in the future.
Post by Nicolas Williams
A timeout or signal could occur before _any_ events occur, in which case
*nget will == 0.
Imagine an alternative port_getn() design where nget is not a pointer
and port_getn() returns the number of arguments fetched. Ah, but to
avoid confusion you'd always have to first set errno = 0 _before calling
port_getn()_, and that'd be obnoxious. Thus (I conjecture) the actual
design of port_getn().
Post by Jeff Trawick
I couldn't reproduce this behavior with port_get().
libevent handles/works around this behavior as follows (see the "errno
if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
&nevents, &ts)) == -1) {
if (errno == EINTR) {
evsignal_process();
return (0);
} else if (errno == ETIME) {
if (nevents == 0)
return (0);
This is correct, not a "workaround". The returned events must get
processed.
Post by Jeff Trawick
} else {
perror("port_getn");
return (-1);
}
[...]
Can anyone advise on how an app can avoid pitfalls in this area
regardless of *Solaris level?
Just like libevent does it.
Maybe ;)
Post by Nicolas Williams
Post by Jeff Trawick
I will open a bug at bugs.opensolaris.org to fix the code or the doc,
unless for some reason shared here it isn't needed or there is a better
way to resolve the issue.
That'd be great, thanks.
Will-do.

Thanks again!
Nicolas Williams
2009-08-19 22:03:50 UTC
Permalink
Post by Jeff Trawick
First, thanks!
NP.
Post by Jeff Trawick
A lot of other people can't code correctly to this type of interface.
Even Sun examples can be wrong
(http://developers.sun.com/solaris/articles/listing2_poll.c).
Ooops! :)
Post by Jeff Trawick
Can you confirm that ETIME and EINTR are the only two -1/errno
indications that can be reported at the same time that one or more
events is returned?
Hmmm, from code inspection I see that the actual port_event_t list[]
entries are copied out in one go when port_getn() returns (see
uts/common/fs/portfs/port.c:port_getn() around lines 1524-1527 and
1561-1562). There'd be no point in trying to validate the list[]
argument any earlier than that. The copyout() can fail, resulting in
EFAULT, but the events have been consumed anyways, and *nget still gets
updated though you obviously lose some of them.

You can get EFAULT if part or all of the list[] array fall outside
mapped address space. Don't do that :)

EBADFD can result if the port is closed at any time while port_getn() is
not done getting events, in which case port_getn() can have consumed
some events, list[] and *nget are updated.

EBADF and EINVAL can only result _before_ consuming any events (EINVAL
occurs if max and *nget conflict, or if the timeout is invalid, because,
e.g., it has too many nanoseconds).
Post by Jeff Trawick
Alternatively, can you state categorically that if an event is returned,
no errors (EBADF, EBADFD, EFAULT, etc.) occurred?
No. See above.
Post by Jeff Trawick
I'd like to fix my code and not worry about some other errno having a
similar issue in the future.
saved_errno = 0;
res = port_getn(port, pevlist, max, &nevents, &timeout);
if (res == -1) {
switch (errno) {
case EINVAL:
/* shouldn't happen; fatal? */
...
case EBADF:
/* port got closed, no events consumed; bail? */
...
case EFAULT:
/* something really bad happened; exit? */
...
}
}
/* If we get here errno == 0, EBADFD, EINTR or ETIME */
saved_errno = errno;
if (nevents >= 0) {
/* handle events returned by port_getn() */
...
}
switch (saved_errno) {
case ETIME:
/* handle timeout */
...
case EINTR:
/* handle interrupt, probably just ignore */
...
case EBADFD:
/* oops! port got closed! bail, re-open, ... */
...
}
Post by Jeff Trawick
Post by Nicolas Williams
Just like libevent does it.
Maybe ;)
Yes, one can do something a bit more involved (see above).

Nico
--
Peter Memishian
2009-08-19 21:29:44 UTC
Permalink
Post by Nicolas Williams
ETIME is returned if the timer expires. Regardless of whether ETIME
happens, *nget gets updated to the number of events returned (if neither ETIME
nor EINTR occur, and *nget <= max, then *nget will not be modified, of
course).
Think of ETIME/EINTR not as an error but as a supplementary bit of
inforation: port_getn() always returns, via the nget input/output
argument, the number of events fetched, and ETIME/EINTR merely indicate
whether a timeout or signal interrupted the wait for more events.
It may be too late to fix this, but I think this is a flawed aspect of the
API. It should return 0 if any events were received in the time alotted;
the caller can then check *nget to determine how many were actually
received. That is, ETIME should be relegated to a hard failure of
receiving no events; pressing it into service for partial failure only
makes programming to the API more error-prone.
--
meem
Nicolas Williams
2009-08-19 22:11:17 UTC
Permalink
Post by Peter Memishian
Post by Nicolas Williams
ETIME is returned if the timer expires. Regardless of whether ETIME
happens, *nget gets updated to the number of events returned (if neither ETIME
nor EINTR occur, and *nget <= max, then *nget will not be modified, of
course).
Think of ETIME/EINTR not as an error but as a supplementary bit of
inforation: port_getn() always returns, via the nget input/output
argument, the number of events fetched, and ETIME/EINTR merely indicate
whether a timeout or signal interrupted the wait for more events.
It may be too late to fix this, but I think this is a flawed aspect of the
IMO it is too late. What we could do is make sure on EINVAL/EBADF *nget
gets set to 0. That would mean that the only error the app has to check
for before checking *nget is EFAULT (EFAULT is probably so fatal that
the app should abort()).
Post by Peter Memishian
API. It should return 0 if any events were received in the time alotted;
the caller can then check *nget to determine how many were actually
received. That is, ETIME should be relegated to a hard failure of
receiving no events; pressing it into service for partial failure only
makes programming to the API more error-prone.
That would require re-queing events. Any errors while trying to do that
would result in lost events. IMO, it's better to not risk that.

The only error resulting in lost events, right now, is EFAULT, and
that's entirely avoidable, and probably fatal to callers whenever it
occurs (i.e., they should probably abort()).

The fact that there are other errors that don't consume events but don't
update *nget is what's really annoying. That is something we could fix.

Nico
--
David Pacheco
2009-08-19 22:24:02 UTC
Permalink
If an event is triggered at about the same time as a timeout expires, the port_getn() libc interface can return with
retcode: -1
errno: ETIME
number of events: updated to > 0
The -1/ETIME must be ignored and the event(s) processed, as the kernel will not report it again until after a subsequent call to port_associate().
I've seen it on 2009.06 and S10U7.
...
ETIME The time interval expired before the expected
number of events have been posted to the port.
...
...
ETIME The time interval expired before the expected
number of events have been posted to the port
(original value in nget), or nget is updated with
the number of returned port_event_t structures in
list[].
The second description of ETIME is perhaps unclear (is it "before A or B occurs" or "before A occurs; otherwise"); regardless, neither description mentions this behavior.
I think it's not quite either of those. I think it's saying that if "the time interval expire[s] before the expected number of events have been posted to the port", then it may return with ETIME or it may set *nget to the number of returned structures. In this case, it's an inclusive "or". The wording is confusing at best. As Meem points out, even if the wording were fixed, the semantic is still a bit odd.
Can anyone advise on how an app can avoid pitfalls in this area regardless of *Solaris level?
Empirically and corroborated by the man page (if you assume what I said above about the wording of the ETIME specification), if you got no error or ETIME, you must check *nget for events. It's unclear whether the value of *nget is defined for EINTR, but I would expect it should be treated the same as ETIME. Of course, there's no way to err on the side of caution here, since the failure mode of getting this wrong in either case is disastrous (missing events or consuming garbage).

The other documented errors (EBADF, EBADFD, EFAULT, EINVAL) are programmer errors you should always be able to avoid and you should probably assume that you got zero events in those cases.

-- Dave
I will open a bug at bugs.opensolaris.org to fix the code or the doc, unless for some reason shared here it isn't needed or there is a better way to resolve the issue.
Thanks!
--
This message posted from opensolaris.org
David Pacheco
2009-08-19 22:48:06 UTC
Permalink
Post by Jeff Trawick
If an event is triggered at about the same time as
a timeout expires, the port_getn() libc interface can
return with
Post by Jeff Trawick
retcode: -1
errno: ETIME
number of events: updated to > 0
The -1/ETIME must be ignored and the event(s)
processed, as the kernel will not report it again
until after a subsequent call to port_associate().
Post by Jeff Trawick
I've seen it on 2009.06 and S10U7.
The port_get() and port_getn() functions will
...
ETIME The time interval expired before
the expected
number of events have been posted to
the port.
...
...
ETIME The time interval expired before the
expected
number of events have been posted
to the port
(original value in nget), or nget is
updated with
the number of returned port_event_t
structures in
list[].
description of ETIME is perhaps unclear (is it
"before A or B occurs" or "before A occurs;
otherwise"); regardless, neither description mentions
this behavior.
I think it's not quite either of those. I think it's
saying that if "the time interval expire[s] before
the expected number of events have been posted to the
port", then it may return with ETIME or it may set
*nget to the number of returned structures. In this
case, it's an inclusive "or". The wording is
confusing at best. As Meem points out, even if the
wording were fixed, the semantic is still a bit odd.
Sorry to reply to my own mail, but it's worse than I thought. The man page is obviously wrong in the most literal reading (we don't return ETIME if nget is updated...), but after looking at the code, I'm not even sure what it's supposed to be saying. The code in the kernel appears to return early with ETIME without consuming events in some cases, but other times it goes and consumes events even after getting ETIME. See:

http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/fs/portfs/port.c#1325

Note the check on line 1339. If it falls through because flags = 0 (which it is in the common case for port_getn), we check for events without modifying error. It's unclear if that's intentional - i.e. is the code wrong in addition to the man page or just the man page?

-- Dave
Post by Jeff Trawick
Can anyone advise on how an app can avoid pitfalls
in this area regardless of *Solaris level?
Empirically and corroborated by the man page (if you
assume what I said above about the wording of the
ETIME specification), if you got no error or ETIME,
you must check *nget for events. It's unclear whether
the value of *nget is defined for EINTR, but I would
expect it should be treated the same as ETIME. Of
course, there's no way to err on the side of caution
here, since the failure mode of getting this wrong in
either case is disastrous (missing events or
consuming garbage).
The other documented errors (EBADF, EBADFD, EFAULT,
EINVAL) are programmer errors you should always be
able to avoid and you should probably assume that you
got zero events in those cases.
-- Dave
Post by Jeff Trawick
I will open a bug at bugs.opensolaris.org to fix
the code or the doc, unless for some reason shared
here it isn't needed or there is a better way to
resolve the issue.
Post by Jeff Trawick
Thanks!
--
This message posted from opensolaris.org
Nicolas Williams
2009-08-19 22:59:49 UTC
Permalink
Post by David Pacheco
Sorry to reply to my own mail, but it's worse than I thought. The man
page is obviously wrong in the most literal reading (we don't return
ETIME if nget is updated...), but after looking at the code, I'm not
even sure what it's supposed to be saying. The code in the kernel
appears to return early with ETIME without consuming events in some
cases, but other times it goes and consumes events even after getting
http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/fs/portfs/port.c#1325
Note the check on line 1339. If it falls through because flags = 0
(which it is in the common case for port_getn), we check for events
without modifying error. It's unclear if that's intentional - i.e. is
the code wrong in addition to the man page or just the man page?
To my reading you have these possible conditions:

- max == 0 and port_getn() returns 0 -> *nget will have been set to the
number of events available.

- port_getn() returns 0, or it returns -1 && errno is one of ETIME,
EINTR, or EBADFD -> events may have been consumed and placed in
list[], and *nget will have been set to the number of consumed
events.

- port_getn() returns -1 && errno is one of EBADF or EINVAL -> no
events will have been consumed, list[] and *nget remain untouched.

- port_getn() returns -1 && errno is EFAULT -> events will have been
consumed, *nget will be set to the number of events consumed, and
list[] is undefined.

Nico
--
David Pacheco
2009-08-20 00:57:04 UTC
Permalink
Post by Nicolas Williams
Post by David Pacheco
Sorry to reply to my own mail, but it's worse than I thought. The man
page is obviously wrong in the most literal reading (we don't return
ETIME if nget is updated...), but after looking at the code, I'm not
even sure what it's supposed to be saying. The code in the kernel
appears to return early with ETIME without consuming events in some
cases, but other times it goes and consumes events even after getting
http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/fs/portfs/port.c#1325
Note the check on line 1339. If it falls through because flags = 0
(which it is in the common case for port_getn), we check for events
without modifying error. It's unclear if that's intentional - i.e. is
the code wrong in addition to the man page or just the man page?
- max == 0 and port_getn() returns 0 -> *nget will have been set to the
number of events available.
Except when you hit 6455223. This unlikely case occurs when some events
on the port are not deliverable to the calling thread (e.g., async i/o
completion after a fork in the child process). This is not relevant to
this discussion, but it's worth noting that there are multiple edge
conditions not handled well by the current implementation.
Post by Nicolas Williams
- port_getn() returns 0, or it returns -1 && errno is one of ETIME,
EINTR, or EBADFD -> events may have been consumed and placed in
list[], and *nget will have been set to the number of consumed
events.
EBADFD can't return with events.

It's unlikely that EINTR would return with events because usually we'll
bail out at line 1338, but maybe we can loop through it multiple times
having consumed events the first time and gotten EINTR later.
Post by Nicolas Williams
- port_getn() returns -1 && errno is one of EBADF or EINVAL -> no
events will have been consumed, list[] and *nget remain untouched.
- port_getn() returns -1 && errno is EFAULT -> events will have been
consumed, *nget will be set to the number of events consumed, and
list[] is undefined.
Yep. But really we should fix the implementation and make the man page
match the implementation. One shouldn't need to inspect kernel code to
know how to handle errors from a syscall. I used to own a few bugs in
this area, but I've removed myself as RE since I don't have the time to
get to them any time soon.

-- Dave
--
David Pacheco, Sun Microsystems Fishworks. http://blogs.sun.com/dap/
Nicolas Williams
2009-08-20 15:48:07 UTC
Permalink
Post by David Pacheco
Post by Nicolas Williams
- max == 0 and port_getn() returns 0 -> *nget will have been set to the
number of events available.
Except when you hit 6455223. This unlikely case occurs when some events
on the port are not deliverable to the calling thread (e.g., async i/o
completion after a fork in the child process). This is not relevant to
this discussion, but it's worth noting that there are multiple edge
conditions not handled well by the current implementation.
Oh, ick.
Post by David Pacheco
Post by Nicolas Williams
- port_getn() returns 0, or it returns -1 && errno is one of ETIME,
EINTR, or EBADFD -> events may have been consumed and placed in
list[], and *nget will have been set to the number of consumed
events.
EBADFD can't return with events.
Ah, I misread the code. The same applies to EINTR, as you point out.
Post by David Pacheco
Post by Nicolas Williams
[...]
Yep. But really we should fix the implementation and make the man page
match the implementation. One shouldn't need to inspect kernel code to
know how to handle errors from a syscall. I used to own a few bugs in
this area, but I've removed myself as RE since I don't have the time to
get to them any time soon.
I agree.

So the only error condition that should return events is ETIME. That at
least simplifies things.

Nico
--
Peter Memishian
2009-08-20 16:30:35 UTC
Permalink
Post by Nicolas Williams
So the only error condition that should return events is ETIME. That at
least simplifies things.
As per my earlier comment, I see no inherent need for events to be
returned with ETIME.
--
meem
Nicolas Williams
2009-08-20 16:43:00 UTC
Permalink
Post by Peter Memishian
Post by Nicolas Williams
So the only error condition that should return events is ETIME. That at
least simplifies things.
As per my earlier comment, I see no inherent need for events to be
returned with ETIME.
Indeed. Making port_getn() never return events on error should be
backwards compatible. I'm all for it.
Jeff Trawick
2009-08-20 17:01:10 UTC
Permalink
Post by Peter Memishian
Post by Nicolas Williams
So the only error condition that should return events is ETIME. That at
least simplifies things.
As per my earlier comment, I see no inherent need for events to be
returned with ETIME.
+1

Some existing code in the wild will start working correctly under timing
scenarios that haven't been properly tested (or perhaps just diagnosed).
Existing, working code will continue to work.
It will be easier to understand/document, and build on knowledge of
existing APIs like poll() and select().
Peter Memishian
2009-08-20 23:56:59 UTC
Permalink
Post by Jeff Trawick
Post by Peter Memishian
Post by Nicolas Williams
So the only error condition that should return events is ETIME. That at
least simplifies things.
As per my earlier comment, I see no inherent need for events to be
returned with ETIME.
+1
Some existing code in the wild will start working correctly under timing
scenarios that haven't been properly tested (or perhaps just diagnosed).
Existing, working code will continue to work.
It will be easier to understand/document, and build on knowledge of
existing APIs like poll() and select().
I've filed 6874410. If we implement it, I think the doc fix becomes
simply to replace the confusing ETIME verbiage in the manpage with:

ETIME The time interval expired before the any events
were posted to the port.
--
meem
Jeff Trawick
2009-08-20 18:32:12 UTC
Permalink
Post by Nicolas Williams
Post by David Pacheco
Post by Nicolas Williams
- max == 0 and port_getn() returns 0 -> *nget will have been set to the
number of events available.
Except when you hit 6455223. This unlikely case occurs when some events
on the port are not deliverable to the calling thread (e.g., async i/o
completion after a fork in the child process). This is not relevant to
this discussion, but it's worth noting that there are multiple edge
conditions not handled well by the current implementation.
Oh, ick.
Post by David Pacheco
Post by Nicolas Williams
- port_getn() returns 0, or it returns -1 && errno is one of ETIME,
EINTR, or EBADFD -> events may have been consumed and placed in
list[], and *nget will have been set to the number of consumed
events.
EBADFD can't return with events.
Ah, I misread the code. The same applies to EINTR, as you point out.
I don't understand the comment here about EINTR. As far as anyone knows,
is it possible to get EINTR with an event, as David suggests in the
previous e-mail? ("It's unlikely that EINTR would return with events
because usually we'll bail out at line 1338, but maybe we can loop
through it multiple times having consumed events the first time and
gotten EINTR later. ")

Thanks!
David Pacheco
2009-08-20 19:42:14 UTC
Permalink
Post by Jeff Trawick
Post by Nicolas Williams
Post by David Pacheco
Post by Nicolas Williams
- max == 0 and port_getn() returns 0 -> *nget will have been set to the
number of events available.
Except when you hit 6455223. This unlikely case occurs when some
events on the port are not deliverable to the calling thread (e.g.,
async i/o completion after a fork in the child process). This is not
relevant to this discussion, but it's worth noting that there are
multiple edge conditions not handled well by the current implementation.
Oh, ick.
Post by David Pacheco
Post by Nicolas Williams
- port_getn() returns 0, or it returns -1 && errno is one of ETIME,
EINTR, or EBADFD -> events may have been consumed and placed in
list[], and *nget will have been set to the number of consumed
events.
EBADFD can't return with events.
Ah, I misread the code. The same applies to EINTR, as you point out.
I don't understand the comment here about EINTR. As far as anyone knows,
is it possible to get EINTR with an event, as David suggests in the
previous e-mail? ("It's unlikely that EINTR would return with events
because usually we'll bail out at line 1338, but maybe we can loop
through it multiple times having consumed events the first time and
gotten EINTR later. ")
I didn't look at the code closely enough to say for sure. I know that we
sometimes call the port_getn() kernel function multiple times for a
single port_getn syscall invocation. If we can leave a port_getn()
function call having consumed some events but reenter in order to
consume more, then you could get EINTR with events...

-- Dave
--
David Pacheco, Sun Microsystems Fishworks. http://blogs.sun.com/dap/
Jeff Trawick
2009-10-01 17:09:20 UTC
Permalink
Post by David Pacheco
Post by Jeff Trawick
Post by Nicolas Williams
Post by David Pacheco
Post by Nicolas Williams
- max == 0 and port_getn() returns 0 -> *nget will have been set to the
number of events available.
Except when you hit 6455223. This unlikely case occurs when some
events on the port are not deliverable to the calling thread (e.g.,
async i/o completion after a fork in the child process). This is
not relevant to this discussion, but it's worth noting that there
are multiple edge conditions not handled well by the current
implementation.
Oh, ick.
Post by David Pacheco
Post by Nicolas Williams
- port_getn() returns 0, or it returns -1 && errno is one of ETIME,
EINTR, or EBADFD -> events may have been consumed and placed in
list[], and *nget will have been set to the number of consumed
events.
EBADFD can't return with events.
Ah, I misread the code. The same applies to EINTR, as you point out.
I don't understand the comment here about EINTR. As far as anyone
knows, is it possible to get EINTR with an event, as David suggests
in the previous e-mail? ("It's unlikely that EINTR would return with
events because usually we'll bail out at line 1338, but maybe we can
loop through it multiple times having consumed events the first time
and gotten EINTR later. ")
I didn't look at the code closely enough to say for sure. I know that
we sometimes call the port_getn() kernel function multiple times for a
single port_getn syscall invocation. If we can leave a port_getn()
function call having consumed some events but reenter in order to
consume more, then you could get EINTR with events...
A crucial detail regarding the issue of events returned with EINTR:

Just because nget > 0 when port_getn() returns -1/EINTR doesn't mean you
have an event. When port_getn() is interrupted with a signal generally
and -1/EINTR is returned, it appears that nget isn't updated to anything
meaningful. So my code sees nget == 1 and tries to process the event.
If no event had ever been returned (list uninitialized), I crash because
portev_user is NULL; if an event had been returned previously in that
storage, I would try to process the same event again, and associate it
again afterwards. (seen on S10U5, b111a, b123)

I may have to set portev_user to something unique before calling
port_getn() then clear nget if -1/EINTR is returned but portev_user is
unchanged. (The user arg to port_associate() is always non-NULL.)

Any thoughts on this aspect?

Jeff Trawick
2009-08-19 23:38:56 UTC
Permalink
Post by Jeff Trawick
I will open a bug at bugs.opensolaris.org to fix the code or the doc,
unless for some reason shared here it isn't needed or there is a
better way to resolve the issue.
CR 6873782 has been opened for the man page issues, with a pointer to
this thread.
Loading...