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
_______________________________________________
gtk-app-devel-list mailing list
gtk-app-devel-list@gnome.org
https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list

Reply via email to