Is this a bug of GMainLoop?

View: New views
8 Messages — Rating Filter:   Alert me  

Is this a bug of GMainLoop?

by #YU KUAN# :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some parts of this message have been removed. Learn more about Nabble's security policy.

I write a client network program using GIOChannel and GMainLoop on windows.

The server program is a simple sink server, which eats all input.

The client program first connects server, then watches for G_IO_IN and G_IO_OUT

and creates a thread which do g_main_loop_run.

At this point G_IO_OUT is surely ok, the mainloop thread returns from poll and sends one byte to server in callback.

Then the program watches for G_IO_IN. mainloop thread will block.

Then the program sleeps for a while, and watches for G_IO_OUT.

Here, I wish the mainloop thread to return from poll with G_IO_OUT event.

However, mainloop will block instead of returning from poll.

 

I think the problem is FD_WRITE will not signaled again until send returns WSAEWOULDBLOCK on windows.

Is this a bug of GMainLoop?

Thanks.

 

The client program source code:

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

#include <winsock2.h>

 

#include "glib.h"

 

 

guint source_id;

GMutex *mutex;

GCond *cond;

 

 

gboolean

recv_cb (GIOChannel *source,

         GIOCondition condition,

         gpointer data)

{

  g_assert_not_reached ();

}

 

gboolean

send_cb (GIOChannel *source,

         GIOCondition condition,

         gpointer data)

{

  gboolean is_second = (gboolean) data;

  GIOStatus status;

  gsize bytes_written;

  gchar c = 'T';

 

  status = g_io_channel_write_chars (source, &c, 1, &bytes_written, NULL);

  g_print ("%s send_cb write char, status: %d.\n",

           (is_second ? "Second" : "First"),

           status);

 

  g_source_remove (source_id);

 

  source_id = g_io_add_watch (source,

                               G_IO_IN | G_IO_ERR,

                               recv_cb,

                               NULL);

 

  g_mutex_lock (mutex);

  g_cond_signal (cond);

  g_mutex_unlock (mutex);

 

  return (TRUE);

}

 

int

main (int argc, char **argv)

{

  WSADATA wsa_data;

  SOCKET s;

  struct sockaddr_in addr;

  gint addrlen = sizeof (addr);

 

  GIOChannel *channel;

  GMainLoop *loop;

  GThread *thread;

 

  g_assert (WSAStartup(0x0202, &wsa_data) == 0);

  g_thread_init (NULL);

 

  mutex = g_mutex_new ();

  cond = g_cond_new ();

 

  s = socket (AF_INET, SOCK_STREAM, 0);

  g_assert (s != INVALID_SOCKET);

 

  g_assert (WSAStringToAddress ("155.69.149.175:5555",

                                  AF_INET,

                                  NULL,

                                  (PSOCKADDR)&addr,

                                  &addrlen)

            != SOCKET_ERROR);

 

  g_assert (connect (s, (struct sockaddr *) &addr, sizeof (addr)) != SOCKET_ERROR);

 

  channel = g_io_channel_win32_new_socket (s);

 

  loop = g_main_loop_new (NULL, FALSE);

  g_assert (loop);

 

  thread = g_thread_create ((GThreadFunc) g_main_loop_run,

                             loop,

                             TRUE,

                             NULL);

  g_assert (thread);

 

 

  g_mutex_lock (mutex);

  source_id = g_io_add_watch (channel,

                               G_IO_IN | G_IO_ERR | G_IO_OUT,

                               send_cb,

                               (gpointer) FALSE);

  g_cond_wait (cond, mutex);

  g_mutex_unlock (mutex);

 

  g_usleep (500000);

 

  g_source_remove (source_id);

 

  g_usleep (500000);

 

  g_mutex_lock (mutex);

  source_id = g_io_add_watch (channel,

                               G_IO_IN | G_IO_ERR | G_IO_OUT,

                               send_cb,

                               (gpointer) TRUE);

  g_cond_wait (cond, mutex);

  g_mutex_unlock (mutex);

 

  g_main_loop_quit (loop);

  g_thread_join (thread);

 

  return (0);

}

 


_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: Is this a bug of GMainLoop?

by Sven Herzberg :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

Am Mittwoch, den 14.05.2008, 09:34 +0800 schrieb #YU KUAN#:

> At this point G_IO_OUT is surely ok, the mainloop thread returns from
> poll and sends one byte to server in callback.
>
> Then the program watches for G_IO_IN. mainloop thread will block.
>
> Then the program sleeps for a while, and watches for G_IO_OUT.
>
> Here, I wish the mainloop thread to return from poll with G_IO_OUT
> event.
>
> However, mainloop will block instead of returning from poll.
>
>  
>
> I think the problem is FD_WRITE will not signaled again until send
> returns WSAEWOULDBLOCK on windows.
>
> Is this a bug of GMainLoop?

Absolutely not; this is a bug in your code (more exactly a missing call
to g_io_channel_set_flags()); check the documentation for proper usage
of this function.

Also, application development questions should be raised on
gtk-app-devel-list (and think of the vast amount of gtk+ based software
out there, it's quite unlikely that you hit a massive bug like this).

Regards,
  Sven

--
Sven Herzberg
Imendio AB - Expert solutions in GTK+
http://www.imendio.com

_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: Is this a bug of GMainLoop?

by Tor Lillqvist :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> and think of the vast amount of gtk+ based software
> out there, it's quite unlikely that you hit a massive bug like this.

I wouldn't be so sure actually. As far as I can see, Yu's sample
program might well use GIOChannels and watches of them in a new way
that hasn't been tested before... I am still debugging it and trying
to understand what is actually going on. There might very well be some
clear bug in giowin32.c that is triggered here. (But whether fixing
that bug so that Yu's program works won't then cause some other
program to fail is another thing...) (There are bug reports open
against giowin32.c that contain patches even that I haven't had the
courage to apply for the same reason...)

That is the problem with the GIOChannel and main loop stuff on
Windows; there are so many ways one can combine the calls that it is
very hard to verify that everything that is supposed to work actually
does. On Unix the GIOChannel and main loop implementations are much
more straightforward, and it is likely that it works whatever
innovative new way a programmer uses it...

--tml
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: Is this a bug of GMainLoop?

by Tor Lillqvist :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Have you, by the way, verified that the corresponding program works on Linux?

--tml
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

RE: Is this a bug of GMainLoop?

by #YU KUAN# :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Thanks for your reply.
Yes, it works in linux.

The following is the log by setting G_IO_WIN32_DEBUG:
1 g_io_channel_win32_new_socket: sockfd=1880
2 g_io_win32_sock_set_flags: NONBLOCK
3 g_io_win32_sock_create_watch: sock=1880 handle=0x73c condition={IN|OUT|ERR}
4 g_io_win32_prepare: WSAEventSelect(1880, 0x73c, {READ|WRITE|ACCEPT|CONNECT|CLOSE}
5 g_io_win32_check: WSAEnumNetworkEvents (1880, 0x73c) revents={IN|OUT} condition={IN|OUT|ERR} events={WRITE|CONNECT}
6 g_io_win32_dispatch: pollfd.revents=OUT condition=IN|OUT|ERR result=OUT
First send_cb write char, status: 1.
7 g_io_win32_sock_create_watch: sock=1880 handle=0x73c condition={IN|ERR}
8 g_io_win32_finalize: channel is for sock=1880
9 g_io_win32_prepare: WSAEventSelect(1880, 0x73c, {READ|ACCEPT|CLOSE}
10 g_io_win32_check: WSAEnumNetworkEvents (1880, 0x73c) revents={IN} condition={IN|ERR} events={}
11 g_io_win32_check: WSAEventSelect(1880, 0x73c, {})
12 g_io_win32_check: ResetEvent(0x73c)
13 g_io_win32_prepare: WSAEventSelect(1880, 0x73c, {READ|ACCEPT|CLOSE}
14 g_io_win32_finalize: channel is for sock=1880
15 g_io_win32_sock_create_watch: sock=1880 handle=0x73c condition={IN|OUT|ERR}
16 g_io_win32_prepare: WSAEventSelect(1880, 0x73c, {READ|WRITE|ACCEPT|CONNECT|CLOSE}

I think the problem is: line 5 shows the g_poll returns FD_WRITE, and according to
          if (watch->pollfd.revents != 0 &&
              events.lNetworkEvents == 0 &&
              !(channel->event_mask & FD_WRITE))
            {
                        ...
                        ResetEvent ((HANDLE) watch->pollfd.fd);
                        ...
                }
The event will not be reset. Since it is not reset, the g_poll will signal this FD_WRITE event again. This is shown in line 10: revents={IN}, but events={}.
And in line 12, the event is reset. So the next g_poll will check FD_WRITE event of the socket. However, according to msdn documentation of WSAEventSelect,
----------------------------
The FD_WRITE network event is handled slightly differently. An FD_WRITE network event is recorded when a socket is first connected with connect/WSAConnect or accepted with accept/WSAAccept, and then after a send fails with WSAEWOULDBLOCK and buffer space becomes available. Therefore, an application can assume that sends are possible starting from the first FD_WRITE network event setting and lasting until a send returns WSAEWOULDBLOCK. After such a failure the application will find out that sends are again possible when an FD_WRITE network event is recorded and the associated event object is set.
----------------------------
The FD_WRITE event of the socket will not be signaled. As a result, g_poll will block and not return.

I add the following in line giowin32.c:841, the program can work.
          channel->event_mask = event_mask;
#if 0
          channel->event = watch->pollfd.fd;
#endif
          channel->last_events = 1;
+        if (event_mask & FD_WRITE && !channel->write_would_have_blocked)
+          WSASetEvent (watch->pollfd.fd);
        }
      break;
But I don't know whether it affects other part of glib program.

Regards,
YuKuan

-----Original Message-----
From: tlillqvist@... [mailto:tlillqvist@...] On Behalf Of Tor Lillqvist
Sent: 星期四, 15 五月, 2008 AM 1:53
To: #YU KUAN#
Cc: gtk-devel-list@...
Subject: Re: Is this a bug of GMainLoop?

Have you, by the way, verified that the corresponding program works on Linux?

--tml
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: Is this a bug of GMainLoop?

by Tor Lillqvist :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> But I don't know whether it affects other part of glib program.

That is indeed the problem;) The code in giowin32.c is extremely
fragile and somewhat ad-hoc. I spent several hours yesterday going
over the debugging output, attempting small changes here and there. (I
didn't come up with your suggested patch, but some similar ideas.) I
will try your suggestion, too, and try to check whether it affects
adversely the use patterns of GIOChannels and main loops in some other
applications.

--tml
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: Is this a bug of GMainLoop?

by Tor Lillqvist :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> I will try your suggestion, too, and try to check whether it affects
> adversely the use patterns of GIOChannels and main loops in some other
> applications.

As far as I could see briefly testing, Yu's patch didn't have any ill
effects. I would love if more people could test it, though.

(There was a slight glitch in the context of the patch in Yu's
message, maybe a copy/paste error? I mean the line
"channel->last_events = 1;" which actually is "channel->last_events =
0;" in the source.)

Yu's patch, to *current* trunk giowin32.c (I just committed a change
that improved the G_IO_WIN32_DEBUG output a bit) is as follows. Please
test it, all who can. (It should be trivial to apply it manually to
the glib-2-16 branch, too.)

--tml

Index: glib/giowin32.c
===================================================================
--- glib/giowin32.c (revision 6898)
+++ glib/giowin32.c (working copy)
@@ -849,6 +849,13 @@
   if (channel->debug)
     g_print ("\n  setting last_events=0");
   channel->last_events = 0;
+
+  if ((event_mask & FD_WRITE) && !channel->write_would_have_blocked)
+    {
+      if (channel->debug)
+ g_print (" WSASetEvent(%#x)", watch->pollfd.fd);
+      WSASetEvent ((WSAEVENT) watch->pollfd.fd);
+    }
  }
       break;
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: Is this a bug of GMainLoop?

by Tor Lillqvist :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> Yu's patch, to *current* trunk giowin32.c (I just committed a change
> that improved the G_IO_WIN32_DEBUG output a bit) is as follows. Please
> test it, all who can. (It should be trivial to apply it manually to
> the glib-2-16 branch, too.)

Hello, anyone ? Please...

--tml

> Index: glib/giowin32.c
> ===================================================================
> --- glib/giowin32.c     (revision 6898)
> +++ glib/giowin32.c     (working copy)
> @@ -849,6 +849,13 @@
>          if (channel->debug)
>            g_print ("\n  setting last_events=0");
>          channel->last_events = 0;
> +
> +         if ((event_mask & FD_WRITE) && !channel->write_would_have_blocked)
> +           {
> +             if (channel->debug)
> +               g_print (" WSASetEvent(%#x)", watch->pollfd.fd);
> +             WSASetEvent ((WSAEVENT) watch->pollfd.fd);
> +           }
>        }
>       break;
>
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list