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 <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2011-09-15 22:58:02 -07:00
parent 5804c2970e
commit 8c18add46b
2 changed files with 54 additions and 43 deletions

View file

@ -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

View file

@ -1,5 +1,6 @@
#include <stdio.h>
#include <gtk/gtk.h>
#include <pthread.h>
#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);
}
/*