Index: chrome/browser/sync/notifier/base/win32/async_network_alive_win32.cc |
=================================================================== |
--- chrome/browser/sync/notifier/base/win32/async_network_alive_win32.cc (revision 0) |
+++ chrome/browser/sync/notifier/base/win32/async_network_alive_win32.cc (revision 0) |
@@ -0,0 +1,233 @@ |
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <winsock2.h> |
+ |
+#include "chrome/browser/sync/notifier/base/async_network_alive.h" |
+#include "chrome/browser/sync/notifier/base/utils.h" |
+#include "talk/base/criticalsection.h" |
+#include "talk/base/logging.h" |
+#include "talk/base/scoped_ptr.h" |
+#include "talk/base/common.h" |
+#include "third_party/smartany/scoped_any.h" |
+ |
+namespace notifier { |
+class PlatformNetworkInfo { |
+ public: |
+ PlatformNetworkInfo() : ws_handle_(NULL), event_handle_(NULL) { |
+ } |
+ |
+ ~PlatformNetworkInfo() { |
+ Close(); |
+ } |
+ |
+ void Close() { |
+ talk_base::CritScope crit_scope(&crit_sect_); |
+ if (ws_handle_) { |
+ if (event_handle_) // unblock any waiting for network changes |
+ SetEvent(get(event_handle_)); |
+ // finishes the iteration. |
+ VERIFY(WSALookupServiceEnd(ws_handle_) == 0); |
+ ws_handle_ = NULL; |
+ LOG_F(LS_INFO) << "WSACleanup 1"; |
+ ::WSACleanup(); |
+ } |
+ } |
+ |
+ bool IsAlive(bool* error) { |
+ ASSERT(error); |
+ *error = false; |
+ |
+ // If IsAlive was previously called, we need a new handle. |
+ // Why? If we use the same handle, we only get diffs on what changed |
+ // which isn't what we want. |
+ Close(); |
+ int result = Initialize(); |
+ if (result != 0) { |
+ LOG_F(LS_ERROR) << "failed:" << result; |
+ // Default to alive on error. |
+ *error = true; |
+ return true; |
+ } |
+ |
+ bool alive = false; |
+ |
+ // Retrieve network info and move to next one. In this function, we only |
+ // need to know whether or not there is network connection. |
+ // allocate 256 bytes for name, it should be enough for most cases. |
+ // If the name is longer, it is OK as we will check the code returned and |
+ // set correct network status. |
+ char result_buffer[sizeof(WSAQUERYSET) + 256] = {0}; |
+ bool flush_previous_result = false; |
+ do { |
+ DWORD control_flags = LUP_RETURN_NAME; |
+ if (flush_previous_result) { |
+ control_flags |= LUP_FLUSHPREVIOUS; |
+ } |
+ DWORD length = sizeof(result_buffer); |
+ reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])->dwSize = |
+ sizeof(WSAQUERYSET); |
+ // ws_handle_ may be NULL (if exiting), but the call will simply fail |
+ int result = ::WSALookupServiceNext( |
+ ws_handle_, |
+ control_flags, |
+ &length, |
+ reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])); |
+ |
+ if (result == 0) { |
+ // get at least one connection, return "connected". |
+ alive = true; |
+ } else { |
+ ASSERT(result == SOCKET_ERROR); |
+ result = ::WSAGetLastError(); |
+ if (result == WSA_E_NO_MORE || result == WSAENOMORE) { |
+ break; |
+ } |
+ |
+ // Error code WSAEFAULT means there is a network connection but the |
+ // result_buffer size is too small to contain the results. The |
+ // variable "length" returned from WSALookupServiceNext is the minimum |
+ // number of bytes required. We do not need to retrieve detail info. |
+ // Return "alive" in this case. |
+ if (result == WSAEFAULT) { |
+ alive = true; |
+ flush_previous_result = true; |
+ } else { |
+ LOG_F(LS_WARNING) << "failed:" << result; |
+ *error = true; |
+ break; |
+ } |
+ } |
+ } while (true); |
+ LOG_F(LS_INFO) << "alive: " << alive; |
+ return alive; |
+ } |
+ |
+ bool WaitForChange() { |
+ // IsAlive must be called first. |
+ int junk1 = 0, junk2 = 0; |
+ DWORD bytes_returned = 0; |
+ int result = SOCKET_ERROR; |
+ { |
+ talk_base::CritScope crit_scope(&crit_sect_); |
+ if (!ws_handle_) |
+ return false; |
+ ASSERT(!event_handle_); |
+ reset(event_handle_, ::CreateEvent(NULL, FALSE, FALSE, NULL)); |
+ if (!event_handle_) { |
+ LOG_F(LS_WARNING) << "failed to CreateEvent"; |
+ return false; |
+ } |
+ WSAOVERLAPPED overlapped = {0}; |
+ overlapped.hEvent = get(event_handle_); |
+ WSACOMPLETION completion; |
+ ::SetZero(completion); |
+ completion.Type = NSP_NOTIFY_EVENT; |
+ completion.Parameters.Event.lpOverlapped = &overlapped; |
+ |
+ LOG_F(LS_INFO) << "calling WSANSPIoctl"; |
+ // Do a non-blocking request for change notification. event_handle_ |
+ // will get signaled when there is a change, so we wait on it later. |
+ // It can also be signaled by Close() in order allow clean termination. |
+ result = ::WSANSPIoctl(ws_handle_, |
+ SIO_NSP_NOTIFY_CHANGE, |
+ &junk1, |
+ 0, |
+ &junk2, |
+ 0, |
+ &bytes_returned, |
+ &completion); |
+ } |
+ if (NO_ERROR != result) { |
+ result = ::WSAGetLastError(); |
+ if (WSA_IO_PENDING != result) { |
+ LOG_F(LS_WARNING) << "failed: " << result; |
+ reset(event_handle_); |
+ return false; |
+ } |
+ } |
+ LOG_F(LS_INFO) << "waiting"; |
+ WaitForSingleObject(get(event_handle_), INFINITE); |
+ reset(event_handle_); |
+ LOG_F(LS_INFO) << "changed"; |
+ return true; |
+ } |
+ |
+ private: |
+ int Initialize() { |
+ WSADATA wsa_data; |
+ LOG_F(LS_INFO) << "calling WSAStartup"; |
+ int result = ::WSAStartup(MAKEWORD(2, 2), &wsa_data); |
+ if (result != ERROR_SUCCESS) { |
+ LOG_F(LS_ERROR) << "failed:" << result; |
+ return result; |
+ } |
+ |
+ WSAQUERYSET query_set = {0}; |
+ query_set.dwSize = sizeof(WSAQUERYSET); |
+ query_set.dwNameSpace = NS_NLA; |
+ // Initiate a client query to iterate through the |
+ // currently connected networks. |
+ if (0 != ::WSALookupServiceBegin(&query_set, LUP_RETURN_ALL, |
+ &ws_handle_)) { |
+ result = ::WSAGetLastError(); |
+ LOG_F(LS_INFO) << "WSACleanup 2"; |
+ ::WSACleanup(); |
+ ASSERT(ws_handle_ == NULL); |
+ ws_handle_ = NULL; |
+ return result; |
+ } |
+ return 0; |
+ } |
+ talk_base::CriticalSection crit_sect_; |
+ HANDLE ws_handle_; |
+ scoped_event event_handle_; |
+ DISALLOW_COPY_AND_ASSIGN(PlatformNetworkInfo); |
+}; |
+ |
+class AsyncNetworkAliveWin32 : public AsyncNetworkAlive { |
+ public: |
+ AsyncNetworkAliveWin32() { |
+ } |
+ |
+ virtual ~AsyncNetworkAliveWin32() { |
+ if (network_info_) { |
+ delete network_info_; |
+ network_info_ = NULL; |
+ } |
+ } |
+ |
+ protected: |
+ // SignalThread Interface |
+ virtual void DoWork() { |
+ if (!network_info_) { |
+ network_info_ = new PlatformNetworkInfo(); |
+ } else { |
+ // Since network_info is set, it means that |
+ // we are suppose to wait for network state changes. |
+ if (!network_info_->WaitForChange()) { |
+ // The wait was aborted so we must be shutting down. |
+ alive_ = false; |
+ error_ = true; |
+ return; |
+ } |
+ } |
+ alive_ = network_info_->IsAlive(&error_); |
+ } |
+ |
+ virtual void OnWorkStop() { |
+ if (network_info_) { |
+ network_info_->Close(); |
+ } |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(AsyncNetworkAliveWin32); |
+}; |
+ |
+AsyncNetworkAlive* AsyncNetworkAlive::Create() { |
+ return new AsyncNetworkAliveWin32(); |
+} |
+ |
+} // namespace notifier |
Property changes on: chrome\browser\sync\notifier\base\win32\async_network_alive_win32.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |