From: Linus Torvalds Date: Fri, 16 Sep 2011 05:58:02 +0000 (-0700) Subject: Do libdivecomputer imports in a separate thread X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=8c18add46b2cc0cdfc8c1095916bce81fbde1f1f;p=ext%2Fsubsurface.git Do libdivecomputer imports in a separate thread This is the hackiest thing ever, unless you count the previous code that was even hackier (and just called the gtk main routine at random places). The libdivecomputer library is not really set up to be part of the gtk main loop, and cannot afford (for example) to have lots of mainloop events while it's parsing. Some dive computers are very timing sensitive for the communication. So just start a thread for doing the libdivecomputer stuff, and just continually call the gtk main loop while that thread is running. I'm sure we could actually use some gtk signalling thing to make the thread exit do the right thing, but instead we just poll the status every 100ms. I did say it was hacky. It does seem to work, though. No more temporary graying out of the windows when they don't react in a timely manner because libdivecomputer does some blocking operation. Signed-off-by: Linus Torvalds --- diff --git a/Makefile b/Makefile index fb84494..2f24eb7 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ subsurface: $(OBJS) $(CC) $(LDFLAGS) -o subsurface $(OBJS) \ `xml2-config --libs` \ `pkg-config --libs gtk+-2.0 glib-2.0 gconf-2.0` \ - $(LIBDIVECOMPUTERARCHIVE) + $(LIBDIVECOMPUTERARCHIVE) -lpthread parse-xml.o: parse-xml.c dive.h $(CC) $(CFLAGS) `pkg-config --cflags glib-2.0` -c `xml2-config --cflags` parse-xml.c diff --git a/libdivecomputer.c b/libdivecomputer.c index 49c4f36..c53a81e 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -1,5 +1,6 @@ #include #include +#include #include "dive.h" #include "divelist.h" @@ -21,18 +22,6 @@ /* handling uemis Zurich SDA files */ #include "uemis.h" -/* - * I'd love to do a while-loop here for pending events, but - * that seems to screw up with the dive computer reading timing. - * - * I may need to spawn a new thread to do the computer - * reading stuff.. - */ -static int run_gtk_mainloop(void) -{ - return gtk_main_iteration_do(0); -} - static void error(const char *fmt, ...) { va_list args; @@ -230,18 +219,15 @@ static int dive_cb(const unsigned char *data, unsigned int size, struct tm tm; struct dive *dive; - /* Christ, this is hacky */ - run_gtk_mainloop(); - rc = create_parser(devdata, &parser); if (rc != PARSER_STATUS_SUCCESS) { - error("Unable to create parser for %s", devdata->name); + fprintf(stderr, "Unable to create parser for %s", devdata->name); return rc; } rc = parser_set_data(parser, data, size); if (rc != PARSER_STATUS_SUCCESS) { - error("Error registering the data."); + fprintf(stderr, "Error registering the data."); parser_destroy(parser); return rc; } @@ -249,7 +235,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, dive = alloc_dive(); rc = parser_get_datetime(parser, &dt); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { - error("Error parsing the datetime."); + fprintf(stderr, "Error parsing the datetime."); parser_destroy (parser); return rc; } @@ -267,7 +253,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, unsigned int divetime = 0; rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { - error("Error parsing the divetime."); + fprintf(stderr, "Error parsing the divetime."); parser_destroy(parser); return rc; } @@ -278,7 +264,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, double maxdepth = 0.0; rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { - error("Error parsing the maxdepth."); + fprintf(stderr, "Error parsing the maxdepth."); parser_destroy(parser); return rc; } @@ -289,14 +275,14 @@ static int dive_cb(const unsigned char *data, unsigned int size, unsigned int ngases = 0; rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { - error("Error parsing the gas mix count."); + fprintf(stderr, "Error parsing the gas mix count."); parser_destroy(parser); return rc; } rc = parse_gasmixes(dive, parser, ngases); if (rc != PARSER_STATUS_SUCCESS) { - error("Error parsing the gas mix."); + fprintf(stderr, "Error parsing the gas mix."); parser_destroy(parser); return rc; } @@ -304,7 +290,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, // Initialize the sample data. rc = parse_samples(&dive, parser); if (rc != PARSER_STATUS_SUCCESS) { - error("Error parsing the samples."); + fprintf(stderr, "Error parsing the samples."); parser_destroy(parser); return rc; } @@ -401,9 +387,6 @@ event_cb(device_t *device, device_event_t event, const void *data, void *userdat const device_clock_t *clock = (device_clock_t *) data; device_data_t *devdata = (device_data_t *) userdata; - /* Christ, this is hacky */ - run_gtk_mainloop(); - switch (event) { case DEVICE_EVENT_WAITING: printf("Event: waiting for user action\n"); @@ -429,52 +412,80 @@ event_cb(device_t *device, device_event_t event, const void *data, void *userdat } } +static int import_thread_done = 0, import_thread_cancelled; + static int cancel_cb(void *userdata) { - return run_gtk_mainloop(); + return import_thread_cancelled; } -static void do_import(device_data_t *data) +static const char *do_libdivecomputer_import(device_data_t *data) { device_t *device = NULL; device_status_t rc; - if (data->type == DEVICE_TYPE_UEMIS) { - return uemis_import(); - } - rc = device_open(data->devname, data->type, &device); - if (rc != DEVICE_STATUS_SUCCESS) { - error("Unable to open %s (%s)", data->name, data->devname); - return; - } + if (rc != DEVICE_STATUS_SUCCESS) + return "Unable to open %s (%s)"; // Register the event handler. int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK; rc = device_set_events(device, events, event_cb, data); if (rc != DEVICE_STATUS_SUCCESS) { - error("Error registering the event handler."); device_close(device); - return; + return "Error registering the event handler."; } // Register the cancellation handler. rc = device_set_cancel(device, cancel_cb, data); if (rc != DEVICE_STATUS_SUCCESS) { - error("Error registering the cancellation handler."); device_close(device); - return; + return "Error registering the cancellation handler."; } rc = import_device_data(device, data); if (rc != DEVICE_STATUS_SUCCESS) { - error("Dive data import error"); device_close(device); - return; + return "Dive data import error"; } device_close(device); + return NULL; +} + +static void *pthread_wrapper(void *_data) +{ + device_data_t *data = _data; + const char *err_string = do_libdivecomputer_import(data); + import_thread_done = 1; + return (void *)err_string; +} + +static void do_import(device_data_t *data) +{ + pthread_t pthread; + void *retval; + + if (data->type == DEVICE_TYPE_UEMIS) + return uemis_import(); + + /* I'm sure there is some better interface for waiting on a thread in a gtk main loop */ + import_thread_done = 0; + pthread_create(&pthread, NULL, pthread_wrapper, data); + while (!import_thread_done) { + while (gtk_events_pending()) { + if (gtk_main_iteration_do(0)) { + import_thread_cancelled = 1; + break; + } + } + usleep(100000); + } + if (pthread_join(pthread, &retval) < 0) + retval = "Odd pthread error return"; + if (retval) + error(retval, data->name, data->devname); } /*