Mercurial > pidgin.yaz
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 } |