Still confused on new thread starting idle functions to update UI.



These darn threads and idle functions still baffle me. I'm sorry to be such a pest.

I want to periodically update a textview as new information comes available. Sometimes this information can 
come in quickly (roughly every tenth of a second). Each update is a single line of text.

The observed bad behavior is that sometimes messages don't appear at all, or appear twice in my textview. 
It's quite unpredictable. Sounds like a race condition.

I spawn a thread from the main program - and from now on I don't talk about main - and this new thread 
prepares messages in a character string. The thread then passes a pointer to the character string to an idle 
function so that the idle function can update a textview in the UI. When done, the idle function stops itself 
by returning a G_SOURCE_REMOVE boolean.

The thread allocates memory for the character string, and the idle function does not free it. Instead, the 
thread free's the memory just before it stops. It waits a bit to make sure the idle function has finished 
using the memory containing the message.

But I lied...

Because there are several messages (roughly 30), the thread actually allocates memory for an *array* of 
character strings. An index is then used to specify which one we're using.

This may seem awkward and unnecessary, but if I just use a single character string, it is possible for the 
thread to replace the contents of the string with the next message while an idle function is still working 
with the previous message.

So instead I rotate through 30 character strings with the assumption that by the time the 30th idle function 
is finished, the character string at index 0 is no longer being used by the first idle function and that 
string can now be re-used.

What I had before which worked was a single character string and a sleep (1) after each g_idle_add(), but 
that made things very sluggish.

In the header file, I have:

****** code snip ******
// Define a struct to contain pointers to textview and message for post_message().
typedef struct _msgdata msgdata;
struct _msgdata {
  char *message;
  GtkWidget *textview;
};

#define MAXBUF 30
****** end code snip ******

In my thread (not main), I have:

****** code snip ******
  int i, indx;
  msgdata *msgdata;

  msgdata = (msgdata *) malloc (MAXBUF * sizeof (msgdata));
  memset (msgdata, 0, MAXBUF * sizeof (msgdata));
  for (i=0; i<MAXBUF; i++) {
    msgdata[i].message = (char *) malloc (1024 * sizeof (char));
    memset (msgdata[i].message, 0, 1024 * sizeof (char));
  }

  indx = 0;
  for loop {

    Do interesting things.

    // Report latest results.
    sprintf (msgdata[indx].message, "Some interesting messages %s %i %f etc\n", various arguments);
    msgdata[indx].textview = data->textview1;
    g_idle_add ((GSourceFunc) post_message, &msgdata[indx]);
    next_msg(&indx);

    if (we're done) break;
  }

  sleep (1);  // Wait for idle functions to finish using character string memory.
  for (i=0; i<MAXBUF; i++) {
    free (msgdata[i].message);
  }
  free (msgdata);

  return (EXIT_SUCCESS);
****** end code snip ******

The ugly helper function which increments the index is:

****** code ******
// Increment message buffer index.
int
next_msg (int *index)
{
  index++;
  if ((*index) > (MAXBUF - 1)) {
    (*index) = 0;
  }

  return (EXIT_SUCCESS);
}
****** end code ******

And the idle function:

****** code ******
// Idle function to add a message to a textview.
// This idle function returns 0 in order to stop.
gboolean
post_message (msgdata *data)
{
  GtkTextBuffer *textbuffer;
  GtkTextIter end;

  textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data->textview));
  gtk_text_buffer_get_end_iter (textbuffer, &end);
  gtk_text_buffer_insert (textbuffer, &end, data->message, -1);
  gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (data->textview), &end, 0.0, FALSE, 0, 0);

  return (G_SOURCE_REMOVE);  // This idle function stops when it returns G_SOURCE_REMOVE.
}
****** end code ******

I would've liked to pass the message string to the idle function by value, because then it would only work 
with its own ephemeral copy of the string, but g_idle_add() only allows me to pass a pointer to data, not the 
data itself.

As always, any advice is appreciated,
Dave


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]