Mercurial > pidgin.yaz
changeset 25041:75f72178e361
Fix 3 different race conditions in the win32 network management functionality.
2 related to missing NLA events that occur in quick succession (also causing
events to be triggered twice in some scenarios)
1 related to calling GTK+ API from a different thread (this probably caused
a crash or two).
Fixes #8229
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Fri, 30 Jan 2009 17:48:47 +0000 |
parents | ea9e0fa89c02 |
children | 1cf4ead4119d ca8934dafd28 |
files | libpurple/network.c |
diffstat | 1 files changed, 72 insertions(+), 46 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/network.c Fri Jan 30 03:27:38 2009 +0000 +++ b/libpurple/network.c Fri Jan 30 17:48:47 2009 +0000 @@ -74,8 +74,12 @@ /* Mutex for the other global vars */ static GStaticMutex mutex = G_STATIC_MUTEX_INIT; -static gboolean network_initialized; -static HANDLE network_change_handle; +static gboolean network_initialized = FALSE; +static HANDLE network_change_handle = NULL; +static int (WSAAPI *MyWSANSPIoctl) ( + HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer, + DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, + LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL; #endif struct _PurpleNetworkListenData { @@ -538,27 +542,28 @@ return FALSE; } +static gboolean _print_debug_msg(gpointer data) { + gchar *msg = data; + purple_debug_warning("network", msg); + g_free(msg); + return FALSE; +} + static gpointer wpurple_network_change_thread(gpointer data) { WSAQUERYSET qs; WSAEVENT *nla_event; - time_t last_trigger = time(NULL); - - int (WSAAPI *MyWSANSPIoctl) ( - HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer, - DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, - LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL; - - if (!(MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) { - g_thread_exit(NULL); - return NULL; - } + time_t last_trigger = time(NULL) - 31; + char buf[4096]; + WSAQUERYSET *res = (LPWSAQUERYSET) buf; + DWORD size; if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) { int errorid = WSAGetLastError(); gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Couldn't create WSA event. " - "Message: %s (%d).\n", msg, errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Couldn't create WSA event. " + "Message: %s (%d).\n", msg, errorid)); g_free(msg); g_thread_exit(NULL); return NULL; @@ -579,30 +584,26 @@ return NULL; } - memset(&qs, 0, sizeof(WSAQUERYSET)); - qs.dwSize = sizeof(WSAQUERYSET); - qs.dwNameSpace = NS_NLA; - if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) { - int errorid = WSAGetLastError(); - gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. " - "NLA service is probably not running. Message: %s (%d).\n", - msg, errorid); - g_free(msg); - WSACloseEvent(nla_event); - g_static_mutex_unlock(&mutex); - g_thread_exit(NULL); - return NULL; + if (network_change_handle == NULL) { + memset(&qs, 0, sizeof(WSAQUERYSET)); + qs.dwSize = sizeof(WSAQUERYSET); + qs.dwNameSpace = NS_NLA; + if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) { + int errorid = WSAGetLastError(); + gchar *msg = g_win32_error_message(errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Couldn't retrieve NLA SP lookup handle. " + "NLA service is probably not running. Message: %s (%d).\n", + msg, errorid)); + g_free(msg); + WSACloseEvent(nla_event); + g_static_mutex_unlock(&mutex); + g_thread_exit(NULL); + return NULL; + } } g_static_mutex_unlock(&mutex); - /* Make sure at least 30 seconds have elapsed since the last - * notification so we don't peg the cpu if this keeps changing. */ - if ((time(NULL) - last_trigger) < 30) - Sleep(30000); - - last_trigger = time(NULL); - memset(&completion, 0, sizeof(WSACOMPLETION)); completion.Type = NSP_NOTIFY_EVENT; overlapped.hEvent = nla_event; @@ -610,18 +611,34 @@ if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) { int errorid = WSAGetLastError(); + if (errorid == WSA_INVALID_HANDLE) { + purple_timeout_add(0, _print_debug_msg, + g_strdup("Invalid NLA handle; resetting.\n")); + g_static_mutex_lock(&mutex); + retval = WSALookupServiceEnd(network_change_handle); + network_change_handle = NULL; + g_static_mutex_unlock(&mutex); + continue; /* WSA_IO_PENDING indicates successful async notification will happen */ - if (errorid != WSA_IO_PENDING) { + } else if (errorid != WSA_IO_PENDING) { gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n", - msg, errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Unable to wait for changes. Message: %s (%d).\n", + msg, errorid)); g_free(msg); } } + /* Make sure at least 30 seconds have elapsed since the last + * notification so we don't peg the cpu if this keeps changing. */ + if ((time(NULL) - last_trigger) < 30) + Sleep(30000); + /* This will block until NLA notifies us */ retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE); + last_trigger = time(NULL); + g_static_mutex_lock(&mutex); if (network_initialized == FALSE) { /* Time to die */ @@ -631,8 +648,14 @@ return NULL; } - retval = WSALookupServiceEnd(network_change_handle); - network_change_handle = NULL; + size = sizeof(buf); + while ((retval = WSALookupServiceNext(network_change_handle, 0, &size, res)) == ERROR_SUCCESS) { + /*purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("thread found network '%s'\n", + res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)"));*/ + size = sizeof(buf); + } + WSAResetEvent(nla_event); g_static_mutex_unlock(&mutex); @@ -768,11 +791,12 @@ if (cnt < 0) /* Assume there is a network */ current_network_count = 1; /* Don't listen for network changes if we can't tell anyway */ - else - { + else { current_network_count = cnt; - if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err)) - purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : ""); + if ((MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) { + if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err)) + purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : ""); + } } #endif @@ -848,10 +872,12 @@ msg, errorid); g_free(msg); } + network_change_handle = NULL; + } g_static_mutex_unlock(&mutex); #endif purple_signal_unregister(purple_network_get_handle(), - "network-configuration-changed"); + "network-configuration-changed"); }