diff options
author | Eric S. Raymond <esr@thyrsus.com> | 2010-04-07 08:37:57 -0400 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 2010-04-07 08:37:57 -0400 |
commit | 0ee8c304ad8466d0e656e50c29692b0302fcbd9c (patch) | |
tree | 5f68d351430159a18f0992c885e08e368bd659c7 /libgpsd_core.c | |
parent | a0f13cb50324d587303402a6ac2713258f056177 (diff) | |
download | gpsd-0ee8c304ad8466d0e656e50c29692b0302fcbd9c.tar.gz |
Fix the select()-spin bug on disconnected devices.
Addresses Berlios tracker bug #14638: 100% cpu when Bluetooth gps device
vanishes. Also a couple of reports on the mailing lists.
The underlying problem here was that:
* Disconnecting a USB device causes reads from it to begin returning 0,
in effect an end-of-file condition.
* select(2) sets an active bit not on "I/O is ready" but on "read would not
block" -- including the end-of-file condition.
* Therefore, select() will spin any time its fd set includes a disconnected
device.
The fix is to check for a zero-length read explicitly and always take the
device out of the active set when that happens.
We were actually doing that here, but the test was defective in two ways:
1. The check for a zero return from gpsd_poll(), indicating I/O error
or zero-length read, needed to be *before* the check for full paccket
rather than after. This effectively disabled it.
2. There was a conditional arm in the gpsd_poll() code that made it ignore
zero-length reads for up to a full cycle. I think this may have been a
fossil from long ago when I experimented with non-blocking reads on
devices.
The fix for the gpsmon spin bug is probably quite similar. That's up next.
Diffstat (limited to 'libgpsd_core.c')
-rw-r--r-- | libgpsd_core.c | 17 |
1 files changed, 4 insertions, 13 deletions
diff --git a/libgpsd_core.c b/libgpsd_core.c index 533ea907..72c637ea 100644 --- a/libgpsd_core.c +++ b/libgpsd_core.c @@ -621,21 +621,12 @@ gps_mask_t gpsd_poll(struct gps_device_t *session) /* update the scoreboard structure from the GPS */ gpsd_report(LOG_RAW+2, "%s sent %zd new characters\n", session->gpsdata.dev.path, newlen); - if (newlen == -1) { /* read error */ - gpsd_report(LOG_INF, "GPS on %s is offline (%lf sec since data)\n", - session->gpsdata.dev.path, - timestamp() - session->gpsdata.online); + if (newlen <= 0) { /* read error or EOF*/ + gpsd_report(LOG_INF, "GPS on %s is offline (%lf sec since data)\n", + session->gpsdata.dev.path, + timestamp() - session->gpsdata.online); session->gpsdata.online = 0; return 0; - } else if (newlen == 0) { /* no new data */ - if (session->device_type != NULL && timestamp()>session->gpsdata.online+session->gpsdata.dev.cycle+1) { - gpsd_report(LOG_INF, "GPS on %s is offline (%lf sec since data)\n", - session->gpsdata.dev.path, - timestamp() - session->gpsdata.online); - session->gpsdata.online = 0; - return 0; - } else - return ONLINE_SET; } else if (session->packet.outbuflen == 0) { /* got new data, but no packet */ gpsd_report(LOG_RAW+3, "New data on %s, not yet a packet\n", session->gpsdata.dev.path); |