Bug: gdk_input_remove and gdk_event_wait



There is a bug in gdk_event_wait().
After the select command the code walks throught the input list and
calls the
user defined event handling routine. But this routine can modify the
inputs list
(when gdk_input_remove is called),  which results in unexpected
behavior.

------------Original Code------------------------
   list = inputs;
   while (list)
     {
       input = list->data;

/* list->next can point to a removed input! */

       list = list->next;

       condition = 0;
       if (FD_ISSET (input->source, &readfds))
  condition |= GDK_INPUT_READ;
       if (FD_ISSET (input->source, &writefds))
  condition |= GDK_INPUT_WRITE;
       if (FD_ISSET (input->source, &exceptfds))
  condition |= GDK_INPUT_EXCEPTION;

/* here is the proplem - this call can modify the list */

       if (condition && input->function)
  (* input->function) (input->data, input->source, condition);
     }
---------------------------------------------

There is no problem with gdk_input_add(), because it alway add inputs
at the beginning of the list. My suggestion is to modify
gdk_input_remove
so that we don't actually remove items from the list. Instead we can
simply
set the tag and the condition of the input to zero. We can then remove
this marked inputs in gdk_event_wait.

Here is the modified code for gdk_input_remove and gdk_event_wait,
which resides in gtk+/gdk/gdk.c

---------------------------------------------
void
gdk_input_remove (gint tag)
{
  GList *list;
  GdkInput *input;

  list = inputs;
  while (list)
    {
      input = list->data;

      if (input->tag == tag)
 {
   if (input->destroy)
     (input->destroy) (input->data);

   input->tag = 0;
   input->condition = 0;

   break;
 }

      list = list->next;
    }
}


static gint
gdk_event_wait (void)
{
  GList *list;
  GList *temp_list;
  GdkInput *input;
  GdkInputCondition condition;
  SELECT_MASK readfds;
  SELECT_MASK writefds;
  SELECT_MASK exceptfds;
  int max_input;
  int nfd;

  /* If there are no events pending we will wait for an event.
   * The time we wait is dependant on the "timer". If no timer
   *  has been specified then we'll block until an event arrives.
   *  If a timer has been specified we'll block until an event
   *  arrives or the timer expires. (This is all done using the
   *  "select" system call).
   */

  if (XPending (gdk_display) == 0)
    {
      FD_ZERO (&readfds);
      FD_ZERO (&writefds);
      FD_ZERO (&exceptfds);

      FD_SET (connection_number, &readfds);
      max_input = connection_number;

      list = inputs;
      while (list)
 {
   input = list->data;

   if (input->tag)
     {
       if (input->condition & GDK_INPUT_READ)
  FD_SET (input->source, &readfds);
       if (input->condition & GDK_INPUT_WRITE)
  FD_SET (input->source, &writefds);
       if (input->condition & GDK_INPUT_EXCEPTION)
  FD_SET (input->source, &exceptfds);

       max_input = MAX (max_input, input->source);
       list = list->next;
     }
   else
     {
       temp_list = list;

       if (list->next)
  list->next->prev = list->prev;
       if (list->prev)
  list->prev->next = list->next;
       if (inputs == list)
  inputs = list->next;

       list = list->next;

       temp_list->next = NULL;
       temp_list->prev = NULL;

       g_free (temp_list->data);
       g_list_free (temp_list);
     }
 }

#ifdef USE_PTHREADS
      if (gdk_using_threads)
 {
   gdk_select_waiting = TRUE;

   FD_SET (gdk_threads_pipe[0], &readfds);
   max_input = MAX (max_input, gdk_threads_pipe[0]);
   gdk_threads_leave ();
 }
#endif

      nfd = select (max_input+1, &readfds, &writefds, &exceptfds,
timerp);

#ifdef USE_PTHREADS
      if (gdk_using_threads)
 {
   gchar c;
   gdk_threads_enter ();
   gdk_select_waiting = FALSE;

   if (FD_ISSET (gdk_threads_pipe[0], &readfds))
     read (gdk_threads_pipe[0], &c, 1);
 }
#endif

      timerp = NULL;
      timer_val = 0;

      if (nfd > 0)
 {
   if (FD_ISSET (connection_number, &readfds))
     {
       if (XPending (gdk_display) == 0)
  {
    if (nfd == 1)
      {
        XNoOp (gdk_display);
        XFlush (gdk_display);
      }
    return FALSE;
  }
       else
  return TRUE;
     }

   list = inputs;
   while (list)
     {
       input = list->data;
       list = list->next;

       condition = 0;
       if (FD_ISSET (input->source, &readfds))
  condition |= GDK_INPUT_READ;
       if (FD_ISSET (input->source, &writefds))
  condition |= GDK_INPUT_WRITE;
       if (FD_ISSET (input->source, &exceptfds))
  condition |= GDK_INPUT_EXCEPTION;

       if (condition && input->function)
  (* input->function) (input->data, input->source, condition);
     }
 }
    }
  else
    return TRUE;

  return FALSE;
}

------------------------------------------



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