Hi, Over the past week I have been asking some of you how to correct a hang on exit with TcpChannels on Windows. To recap instantiating a TcpChannel runs a background thread which calls Socket.Accept and blocks. During shutdown a call to mono_thread_manage executes an APC on the waiting thread and then (mono_thread_manage) waits for the blocking thread to exit. The problem is that the APC call does not cause the accept call to break.
There were a couple of suggestions about how to solve this problem. Some were specific to accept and others were general patterns 'un-stick' blocking calls in the runtime. I believe that have tried them all. Some did not work while others caused other complications. The one I decided on was to call select with a timeout to determine if a following accept call would succeed immediately. Consider it a peek on the socket. If the select times out and no connection is present I check the thread state for StopRequested. (Not Abort intentionally.) Finally this is an a infinite loop until a connection is present or the thread is stopped. The loop is only applicable to Windows as I do not observe a problem on Linux. One thing that I had to change was the signature to the internal accept call. I need to know the blocking state of the socket. If a socket is in blocking mode I can go into the loop. If the socket is in non-blocking mode I can not use the loop because it will cause the call to accept to block on a non-blocking socket. The reason I have to pass it into the internal call is because after much searching I can not find a way to query the blocking state of a socket. (There is no ret = fcntl(fd, F_GETFL, 0); on Windows?) If anyone knows how to query this state let me know and I will be happy to change it. -bill 2008-12-17 Bill Holmes <billholme...@gmail.com> * Socket.cs (Accept_internal) : Changing the signature to pass the blocking state. * socket-io.h : Changing the signature of ves_icall_System_Net_Sockets_Socket_Accept_internal to pass the blocking state. * icall-def.h : Changing the signature of System.Net.Sockets.Socket.Accept_internal to pass the blocking state. * socket-io.c (ves_icall_System_Net_Sockets_Socket_Accept_internal) : For Windows only. Avoid blocking when calling accept by querying for a connection via select. The loop also queries the thread state every 1000 micro seconds for the thread stop state. This will avoid the process hanging on shutdown when using a TcpChannel that is never connected to. Code is contributed under MIT/X11 license.
Index: mcs/class/System/System.Net.Sockets/Socket.cs =================================================================== --- mcs/class/System/System.Net.Sockets/Socket.cs (revision 121718) +++ mcs/class/System/System.Net.Sockets/Socket.cs (working copy) @@ -1381,7 +1381,7 @@ // Creates a new system socket, returning the handle [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static IntPtr Accept_internal(IntPtr sock, out int error); + private extern static IntPtr Accept_internal(IntPtr sock, out int error, bool blocking); Thread blocking_thread; public Socket Accept() { @@ -1392,7 +1392,7 @@ IntPtr sock = (IntPtr) (-1); blocking_thread = Thread.CurrentThread; try { - sock = Accept_internal(socket, out error); + sock = Accept_internal(socket, out error, blocking); } catch (ThreadAbortException) { if (disposed) { #if !NET_2_1 @@ -1425,7 +1425,7 @@ blocking_thread = Thread.CurrentThread; try { - sock = Accept_internal (socket, out error); + sock = Accept_internal (socket, out error, blocking); } catch (ThreadAbortException) { if (disposed) { #if !NET_2_1 Index: mcs/class/System/System.Net.Sockets/ChangeLog =================================================================== --- mcs/class/System/System.Net.Sockets/ChangeLog (revision 121718) +++ mcs/class/System/System.Net.Sockets/ChangeLog (working copy) @@ -1,4 +1,11 @@ +2008-12-17 Bill Holmes <billholme...@gmail.com> + + * Socket.cs (Accept_internal) : Changing the signature to pass + the blocking state. + + Code is contributed under MIT/X11 license. + 2008-12-03 Gonzalo Paniagua Javier <gonz...@novell.com> * UdpClient.cs: don't Poll() in Receive(), the call to ReceiveFrom Index: mono/mono/metadata/socket-io.h =================================================================== --- mono/mono/metadata/socket-io.h (revision 121718) +++ mono/mono/metadata/socket-io.h (working copy) @@ -194,7 +194,7 @@ extern gint32 ves_icall_System_Net_Sockets_SocketException_WSAGetLastError_internal(void) MONO_INTERNAL; extern gint32 ves_icall_System_Net_Sockets_Socket_Available_internal(SOCKET sock, gint32 *error) MONO_INTERNAL; extern void ves_icall_System_Net_Sockets_Socket_Blocking_internal(SOCKET sock, gboolean block, gint32 *error) MONO_INTERNAL; -extern gpointer ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock, gint32 *error) MONO_INTERNAL; +extern gpointer ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock, gint32 *error, gboolean blocking) MONO_INTERNAL; extern void ves_icall_System_Net_Sockets_Socket_Listen_internal(SOCKET sock, guint32 backlog, gint32 *error) MONO_INTERNAL; extern MonoObject *ves_icall_System_Net_Sockets_Socket_LocalEndPoint_internal(SOCKET sock, gint32 *error) MONO_INTERNAL; extern MonoObject *ves_icall_System_Net_Sockets_Socket_RemoteEndPoint_internal(SOCKET sock, gint32 *error) MONO_INTERNAL; Index: mono/mono/metadata/ChangeLog =================================================================== --- mono/mono/metadata/ChangeLog (revision 121718) +++ mono/mono/metadata/ChangeLog (working copy) @@ -1,3 +1,21 @@ +2008-12-17 Bill Holmes <billholme...@gmail.com> + + * socket-io.h : Changing the signature of + ves_icall_System_Net_Sockets_Socket_Accept_internal to pass + the blocking state. + + * icall-def.h : Changing the signature of + System.Net.Sockets.Socket.Accept_internal to pass the blocking state. + + * socket-io.c (ves_icall_System_Net_Sockets_Socket_Accept_internal) : + For Windows only. Avoid blocking when calling accept by + querying for a connection via select. The loop also queries + the thread state every 1000 micro seconds for the thread + stop state. This will avoid the process hanging on shutdown + when using a TcpChannel that is never connected to. + + Code is contributed under MIT/X11 license. + 2008-12-17 Miguel de Icaza <mig...@novell.com> * icall.c (ves_icall_System_Environment_get_Platform): For Index: mono/mono/metadata/icall-def.h =================================================================== --- mono/mono/metadata/icall-def.h (revision 121718) +++ mono/mono/metadata/icall-def.h (working copy) @@ -401,7 +401,7 @@ ICALL(NDNS_3, "GetHostName_internal(string&)", ves_icall_System_Net_Dns_GetHostName_internal) ICALL_TYPE(SOCK, "System.Net.Sockets.Socket", SOCK_1) -ICALL(SOCK_1, "Accept_internal(intptr,int&)", ves_icall_System_Net_Sockets_Socket_Accept_internal) +ICALL(SOCK_1, "Accept_internal(intptr,int&,bool)", ves_icall_System_Net_Sockets_Socket_Accept_internal) ICALL(SOCK_2, "Available_internal(intptr,int&)", ves_icall_System_Net_Sockets_Socket_Available_internal) ICALL(SOCK_3, "Bind_internal(intptr,System.Net.SocketAddress,int&)", ves_icall_System_Net_Sockets_Socket_Bind_internal) ICALL(SOCK_4, "Blocking_internal(intptr,bool,int&)", ves_icall_System_Net_Sockets_Socket_Blocking_internal) Index: mono/mono/metadata/socket-io.c =================================================================== --- mono/mono/metadata/socket-io.c (revision 121718) +++ mono/mono/metadata/socket-io.c (working copy) @@ -811,13 +811,41 @@ } gpointer ves_icall_System_Net_Sockets_Socket_Accept_internal(SOCKET sock, - gint32 *error) + gint32 *error, + gboolean blocking) { SOCKET newsock; MONO_ARCH_SAVE_REGS; *error = 0; + +#ifdef PLATFORM_WIN32 + /* Several applications are getting stuck during shutdown on Windows + * when an accept call is on a background thread. + * + */ + if (blocking) { + MonoThread* curthread = mono_thread_current (); + + if (curthread) { + for (;;) { + int selectret; + TIMEVAL timeout; + fd_set readfds; + FD_ZERO (&readfds); + FD_SET(sock, &readfds); + timeout.tv_sec = 0; + timeout.tv_usec = 1000; + selectret = select (0, &readfds, NULL, NULL, &timeout); + if (selectret > 0) + break; + if (curthread->state & ThreadState_StopRequested) + return NULL; + } + } + } +#endif newsock = _wapi_accept (sock, NULL, 0); if(newsock==INVALID_SOCKET) {
_______________________________________________ Mono-devel-list mailing list Mono-devel-list@lists.ximian.com http://lists.ximian.com/mailman/listinfo/mono-devel-list