comparison libpurple/network.c @ 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 a6742d9eadf3
children 4b8c4870b13a af42303654a5
comparison
equal deleted inserted replaced
25034:ea9e0fa89c02 25041:75f72178e361
72 #elif defined _WIN32 72 #elif defined _WIN32
73 static int current_network_count; 73 static int current_network_count;
74 74
75 /* Mutex for the other global vars */ 75 /* Mutex for the other global vars */
76 static GStaticMutex mutex = G_STATIC_MUTEX_INIT; 76 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
77 static gboolean network_initialized; 77 static gboolean network_initialized = FALSE;
78 static HANDLE network_change_handle; 78 static HANDLE network_change_handle = NULL;
79 static int (WSAAPI *MyWSANSPIoctl) (
80 HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer,
81 DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
82 LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL;
79 #endif 83 #endif
80 84
81 struct _PurpleNetworkListenData { 85 struct _PurpleNetworkListenData {
82 int listenfd; 86 int listenfd;
83 int socket_type; 87 int socket_type;
536 current_network_count = new_count; 540 current_network_count = new_count;
537 541
538 return FALSE; 542 return FALSE;
539 } 543 }
540 544
545 static gboolean _print_debug_msg(gpointer data) {
546 gchar *msg = data;
547 purple_debug_warning("network", msg);
548 g_free(msg);
549 return FALSE;
550 }
551
541 static gpointer wpurple_network_change_thread(gpointer data) 552 static gpointer wpurple_network_change_thread(gpointer data)
542 { 553 {
543 WSAQUERYSET qs; 554 WSAQUERYSET qs;
544 WSAEVENT *nla_event; 555 WSAEVENT *nla_event;
545 time_t last_trigger = time(NULL); 556 time_t last_trigger = time(NULL) - 31;
546 557 char buf[4096];
547 int (WSAAPI *MyWSANSPIoctl) ( 558 WSAQUERYSET *res = (LPWSAQUERYSET) buf;
548 HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer, 559 DWORD size;
549 DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
550 LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL;
551
552 if (!(MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) {
553 g_thread_exit(NULL);
554 return NULL;
555 }
556 560
557 if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) { 561 if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) {
558 int errorid = WSAGetLastError(); 562 int errorid = WSAGetLastError();
559 gchar *msg = g_win32_error_message(errorid); 563 gchar *msg = g_win32_error_message(errorid);
560 purple_debug_warning("network", "Couldn't create WSA event. " 564 purple_timeout_add(0, _print_debug_msg,
561 "Message: %s (%d).\n", msg, errorid); 565 g_strdup_printf("Couldn't create WSA event. "
566 "Message: %s (%d).\n", msg, errorid));
562 g_free(msg); 567 g_free(msg);
563 g_thread_exit(NULL); 568 g_thread_exit(NULL);
564 return NULL; 569 return NULL;
565 } 570 }
566 571
577 g_static_mutex_unlock(&mutex); 582 g_static_mutex_unlock(&mutex);
578 g_thread_exit(NULL); 583 g_thread_exit(NULL);
579 return NULL; 584 return NULL;
580 } 585 }
581 586
582 memset(&qs, 0, sizeof(WSAQUERYSET)); 587 if (network_change_handle == NULL) {
583 qs.dwSize = sizeof(WSAQUERYSET); 588 memset(&qs, 0, sizeof(WSAQUERYSET));
584 qs.dwNameSpace = NS_NLA; 589 qs.dwSize = sizeof(WSAQUERYSET);
585 if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) { 590 qs.dwNameSpace = NS_NLA;
591 if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) {
592 int errorid = WSAGetLastError();
593 gchar *msg = g_win32_error_message(errorid);
594 purple_timeout_add(0, _print_debug_msg,
595 g_strdup_printf("Couldn't retrieve NLA SP lookup handle. "
596 "NLA service is probably not running. Message: %s (%d).\n",
597 msg, errorid));
598 g_free(msg);
599 WSACloseEvent(nla_event);
600 g_static_mutex_unlock(&mutex);
601 g_thread_exit(NULL);
602 return NULL;
603 }
604 }
605 g_static_mutex_unlock(&mutex);
606
607 memset(&completion, 0, sizeof(WSACOMPLETION));
608 completion.Type = NSP_NOTIFY_EVENT;
609 overlapped.hEvent = nla_event;
610 completion.Parameters.Event.lpOverlapped = &overlapped;
611
612 if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) {
586 int errorid = WSAGetLastError(); 613 int errorid = WSAGetLastError();
587 gchar *msg = g_win32_error_message(errorid); 614 if (errorid == WSA_INVALID_HANDLE) {
588 purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. " 615 purple_timeout_add(0, _print_debug_msg,
589 "NLA service is probably not running. Message: %s (%d).\n", 616 g_strdup("Invalid NLA handle; resetting.\n"));
590 msg, errorid); 617 g_static_mutex_lock(&mutex);
591 g_free(msg); 618 retval = WSALookupServiceEnd(network_change_handle);
592 WSACloseEvent(nla_event); 619 network_change_handle = NULL;
593 g_static_mutex_unlock(&mutex); 620 g_static_mutex_unlock(&mutex);
594 g_thread_exit(NULL); 621 continue;
595 return NULL; 622 /* WSA_IO_PENDING indicates successful async notification will happen */
596 } 623 } else if (errorid != WSA_IO_PENDING) {
597 g_static_mutex_unlock(&mutex); 624 gchar *msg = g_win32_error_message(errorid);
625 purple_timeout_add(0, _print_debug_msg,
626 g_strdup_printf("Unable to wait for changes. Message: %s (%d).\n",
627 msg, errorid));
628 g_free(msg);
629 }
630 }
598 631
599 /* Make sure at least 30 seconds have elapsed since the last 632 /* Make sure at least 30 seconds have elapsed since the last
600 * notification so we don't peg the cpu if this keeps changing. */ 633 * notification so we don't peg the cpu if this keeps changing. */
601 if ((time(NULL) - last_trigger) < 30) 634 if ((time(NULL) - last_trigger) < 30)
602 Sleep(30000); 635 Sleep(30000);
603 636
604 last_trigger = time(NULL);
605
606 memset(&completion, 0, sizeof(WSACOMPLETION));
607 completion.Type = NSP_NOTIFY_EVENT;
608 overlapped.hEvent = nla_event;
609 completion.Parameters.Event.lpOverlapped = &overlapped;
610
611 if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) {
612 int errorid = WSAGetLastError();
613 /* WSA_IO_PENDING indicates successful async notification will happen */
614 if (errorid != WSA_IO_PENDING) {
615 gchar *msg = g_win32_error_message(errorid);
616 purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n",
617 msg, errorid);
618 g_free(msg);
619 }
620 }
621
622 /* This will block until NLA notifies us */ 637 /* This will block until NLA notifies us */
623 retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE); 638 retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE);
639
640 last_trigger = time(NULL);
624 641
625 g_static_mutex_lock(&mutex); 642 g_static_mutex_lock(&mutex);
626 if (network_initialized == FALSE) { 643 if (network_initialized == FALSE) {
627 /* Time to die */ 644 /* Time to die */
628 WSACloseEvent(nla_event); 645 WSACloseEvent(nla_event);
629 g_static_mutex_unlock(&mutex); 646 g_static_mutex_unlock(&mutex);
630 g_thread_exit(NULL); 647 g_thread_exit(NULL);
631 return NULL; 648 return NULL;
632 } 649 }
633 650
634 retval = WSALookupServiceEnd(network_change_handle); 651 size = sizeof(buf);
635 network_change_handle = NULL; 652 while ((retval = WSALookupServiceNext(network_change_handle, 0, &size, res)) == ERROR_SUCCESS) {
653 /*purple_timeout_add(0, _print_debug_msg,
654 g_strdup_printf("thread found network '%s'\n",
655 res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)"));*/
656 size = sizeof(buf);
657 }
658
636 WSAResetEvent(nla_event); 659 WSAResetEvent(nla_event);
637 g_static_mutex_unlock(&mutex); 660 g_static_mutex_unlock(&mutex);
638 661
639 purple_timeout_add(0, wpurple_network_change_thread_cb, NULL); 662 purple_timeout_add(0, wpurple_network_change_thread_cb, NULL);
640 } 663 }
766 789
767 network_initialized = TRUE; 790 network_initialized = TRUE;
768 if (cnt < 0) /* Assume there is a network */ 791 if (cnt < 0) /* Assume there is a network */
769 current_network_count = 1; 792 current_network_count = 1;
770 /* Don't listen for network changes if we can't tell anyway */ 793 /* Don't listen for network changes if we can't tell anyway */
771 else 794 else {
772 {
773 current_network_count = cnt; 795 current_network_count = cnt;
774 if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err)) 796 if ((MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) {
775 purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : ""); 797 if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err))
798 purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : "");
799 }
776 } 800 }
777 #endif 801 #endif
778 802
779 purple_prefs_add_none ("/purple/network"); 803 purple_prefs_add_none ("/purple/network");
780 purple_prefs_add_bool ("/purple/network/auto_ip", TRUE); 804 purple_prefs_add_bool ("/purple/network/auto_ip", TRUE);
846 gchar *msg = g_win32_error_message(errorid); 870 gchar *msg = g_win32_error_message(errorid);
847 purple_debug_warning("network", "Unable to kill NLA thread. Message: %s (%d).\n", 871 purple_debug_warning("network", "Unable to kill NLA thread. Message: %s (%d).\n",
848 msg, errorid); 872 msg, errorid);
849 g_free(msg); 873 g_free(msg);
850 } 874 }
875 network_change_handle = NULL;
876
851 } 877 }
852 g_static_mutex_unlock(&mutex); 878 g_static_mutex_unlock(&mutex);
853 879
854 #endif 880 #endif
855 purple_signal_unregister(purple_network_get_handle(), 881 purple_signal_unregister(purple_network_get_handle(),
856 "network-configuration-changed"); 882 "network-configuration-changed");
857 } 883 }