Insufficient main loop iteration speed

Everything seems to be running perfectly fine, but after 10 minutes or so, I realize that there is buffering meaning that the data displayed is not realtime, even if I plug-out the USB, some data keeps being displayed.

If you have to display real-time data, you can avoid queuing data altogether. Just enable a VSync-locked animation on the widget and show the very latest data retrieved by the USB acquisition thread.

That can be done with Gtk.Widget.add_tick_callback. Note: if you have multiple widgets that should update in real time, simply add the tick callback to one of them. Then update all the widgets from your tick callback.

/* This data structure contains the latest retrieved data
 * from the USB device. It's written from the USB thread
 * and read from UI thread. Concurrent access is protected
 * by shared_data_mutex.
 */
struct {
  int foo;
  int bar;
} shared_data;
GMutex shared_data_mutex;

/* The GTK widget showing real time USB data */
GtkWidget *usb_widget;
guint usb_widget_tick_id;

/* thread: UI
 *
 * Runs at each VSync
 */
static gboolean
usb_widget_tick_callback (GtkWidget      *widget,
                          GdkFrameClock  *frame_clock,
                          gpointer        user_data)
{
  /* Update the UI with whatever you have in shared_data.
   * In this case USB widget is a simple label */

  g_mutex_lock (&shared_data_mutex);
  char *text = g_strdup_printf ("foo: %d, bar: %d",
                                shared_data.foo,
                                shared_data.bar);
  gtk_label_set_text (GTK_LABEL (usb_widget), text);
  g_ free (text);
  g_mutex_unlock (&shared_data_mutex);

  return G_SOURCE_CONTINUE;
}

/* thread: UI */
static void
usb_widget_tick_callback_start (gpointer user_data)
{
  usb_widget_tick_id = gtk_widget_add_tick_callback (usb_widget, usb_widget_tick_callback, NULL, NULL);
}

/* thread: UI */
static void
usb_widget_tick_callback_stop (gpointer user_data)
{
  gtk_widget_remove_tick_callback (usb_widget, usb_widget_tick_id);
  usb_widget_tick_id = 0;
}

/* thread: USB */
static void
usb_acquisition_starting (void)
{
  /* Start an animation on the USB widget */
  g_idle_add_once (usb_widget_tick_callback_start, NULL);
}

/* thread: USB */
static void
usb_acquisition_have_new_data (const USBData *data)
{
  /* Update latest data for UI thread */
  g_mutex_lock (&shared_data_mutex);

  shared_data.foo = data->foo;
  shared_data.bar = data->bar;

  g_mutex_unlock (&shared_data_mutex);
}

/* thread: USB */
static void
usb_acquisition_ending (void)
{
  /* Stop the animation on the USB widget */
  g_idle_add_once (usb_widget_tick_callback_stop, NULL);
}

static void
app_activate (GtkApplication *app)
{
  GtkWidget *window = gtk_application_window_new (app);

  usb_widget = gtk_label_new (NULL);
  gtk_window_set_child (GTK_WINDOW (window), usb_widget);

  gtk_window_present (GTK_WINDOW (window));

  /* start USB thread with callbacks for notification
   * when data acquisition starts, ends, new data has
   * arrived */
  start_usb_thread (...);
}

int main (int argc, char *argv[]) {
  GtkApplication *app = gtk_application_new ("com.example.GtkApplication",
                                             G_APPLICATION_FLAGS_NONE);

  g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
  return g_application_run (G_APPLICATION (app), argc, argv);
}
2 Likes