OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <winsock2.h> |
| 6 |
| 7 #include "chrome/browser/sync/notifier/base/async_network_alive.h" |
| 8 #include "chrome/browser/sync/notifier/base/utils.h" |
| 9 #include "talk/base/criticalsection.h" |
| 10 #include "talk/base/logging.h" |
| 11 #include "talk/base/scoped_ptr.h" |
| 12 #include "talk/base/common.h" |
| 13 #include "third_party/smartany/scoped_any.h" |
| 14 |
| 15 namespace notifier { |
| 16 class PlatformNetworkInfo { |
| 17 public: |
| 18 PlatformNetworkInfo() : ws_handle_(NULL), event_handle_(NULL) { |
| 19 } |
| 20 |
| 21 ~PlatformNetworkInfo() { |
| 22 Close(); |
| 23 } |
| 24 |
| 25 void Close() { |
| 26 talk_base::CritScope crit_scope(&crit_sect_); |
| 27 if (ws_handle_) { |
| 28 if (event_handle_) // unblock any waiting for network changes |
| 29 SetEvent(get(event_handle_)); |
| 30 // finishes the iteration. |
| 31 VERIFY(WSALookupServiceEnd(ws_handle_) == 0); |
| 32 ws_handle_ = NULL; |
| 33 LOG_F(LS_INFO) << "WSACleanup 1"; |
| 34 ::WSACleanup(); |
| 35 } |
| 36 } |
| 37 |
| 38 bool IsAlive(bool* error) { |
| 39 ASSERT(error); |
| 40 *error = false; |
| 41 |
| 42 // If IsAlive was previously called, we need a new handle. |
| 43 // Why? If we use the same handle, we only get diffs on what changed |
| 44 // which isn't what we want. |
| 45 Close(); |
| 46 int result = Initialize(); |
| 47 if (result != 0) { |
| 48 LOG_F(LS_ERROR) << "failed:" << result; |
| 49 // Default to alive on error. |
| 50 *error = true; |
| 51 return true; |
| 52 } |
| 53 |
| 54 bool alive = false; |
| 55 |
| 56 // Retrieve network info and move to next one. In this function, we only |
| 57 // need to know whether or not there is network connection. |
| 58 // allocate 256 bytes for name, it should be enough for most cases. |
| 59 // If the name is longer, it is OK as we will check the code returned and |
| 60 // set correct network status. |
| 61 char result_buffer[sizeof(WSAQUERYSET) + 256] = {0}; |
| 62 bool flush_previous_result = false; |
| 63 do { |
| 64 DWORD control_flags = LUP_RETURN_NAME; |
| 65 if (flush_previous_result) { |
| 66 control_flags |= LUP_FLUSHPREVIOUS; |
| 67 } |
| 68 DWORD length = sizeof(result_buffer); |
| 69 reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])->dwSize = |
| 70 sizeof(WSAQUERYSET); |
| 71 // ws_handle_ may be NULL (if exiting), but the call will simply fail |
| 72 int result = ::WSALookupServiceNext( |
| 73 ws_handle_, |
| 74 control_flags, |
| 75 &length, |
| 76 reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])); |
| 77 |
| 78 if (result == 0) { |
| 79 // get at least one connection, return "connected". |
| 80 alive = true; |
| 81 } else { |
| 82 ASSERT(result == SOCKET_ERROR); |
| 83 result = ::WSAGetLastError(); |
| 84 if (result == WSA_E_NO_MORE || result == WSAENOMORE) { |
| 85 break; |
| 86 } |
| 87 |
| 88 // Error code WSAEFAULT means there is a network connection but the |
| 89 // result_buffer size is too small to contain the results. The |
| 90 // variable "length" returned from WSALookupServiceNext is the minimum |
| 91 // number of bytes required. We do not need to retrieve detail info. |
| 92 // Return "alive" in this case. |
| 93 if (result == WSAEFAULT) { |
| 94 alive = true; |
| 95 flush_previous_result = true; |
| 96 } else { |
| 97 LOG_F(LS_WARNING) << "failed:" << result; |
| 98 *error = true; |
| 99 break; |
| 100 } |
| 101 } |
| 102 } while (true); |
| 103 LOG_F(LS_INFO) << "alive: " << alive; |
| 104 return alive; |
| 105 } |
| 106 |
| 107 bool WaitForChange() { |
| 108 // IsAlive must be called first. |
| 109 int junk1 = 0, junk2 = 0; |
| 110 DWORD bytes_returned = 0; |
| 111 int result = SOCKET_ERROR; |
| 112 { |
| 113 talk_base::CritScope crit_scope(&crit_sect_); |
| 114 if (!ws_handle_) |
| 115 return false; |
| 116 ASSERT(!event_handle_); |
| 117 reset(event_handle_, ::CreateEvent(NULL, FALSE, FALSE, NULL)); |
| 118 if (!event_handle_) { |
| 119 LOG_F(LS_WARNING) << "failed to CreateEvent"; |
| 120 return false; |
| 121 } |
| 122 WSAOVERLAPPED overlapped = {0}; |
| 123 overlapped.hEvent = get(event_handle_); |
| 124 WSACOMPLETION completion; |
| 125 ::SetZero(completion); |
| 126 completion.Type = NSP_NOTIFY_EVENT; |
| 127 completion.Parameters.Event.lpOverlapped = &overlapped; |
| 128 |
| 129 LOG_F(LS_INFO) << "calling WSANSPIoctl"; |
| 130 // Do a non-blocking request for change notification. event_handle_ |
| 131 // will get signaled when there is a change, so we wait on it later. |
| 132 // It can also be signaled by Close() in order allow clean termination. |
| 133 result = ::WSANSPIoctl(ws_handle_, |
| 134 SIO_NSP_NOTIFY_CHANGE, |
| 135 &junk1, |
| 136 0, |
| 137 &junk2, |
| 138 0, |
| 139 &bytes_returned, |
| 140 &completion); |
| 141 } |
| 142 if (NO_ERROR != result) { |
| 143 result = ::WSAGetLastError(); |
| 144 if (WSA_IO_PENDING != result) { |
| 145 LOG_F(LS_WARNING) << "failed: " << result; |
| 146 reset(event_handle_); |
| 147 return false; |
| 148 } |
| 149 } |
| 150 LOG_F(LS_INFO) << "waiting"; |
| 151 WaitForSingleObject(get(event_handle_), INFINITE); |
| 152 reset(event_handle_); |
| 153 LOG_F(LS_INFO) << "changed"; |
| 154 return true; |
| 155 } |
| 156 |
| 157 private: |
| 158 int Initialize() { |
| 159 WSADATA wsa_data; |
| 160 LOG_F(LS_INFO) << "calling WSAStartup"; |
| 161 int result = ::WSAStartup(MAKEWORD(2, 2), &wsa_data); |
| 162 if (result != ERROR_SUCCESS) { |
| 163 LOG_F(LS_ERROR) << "failed:" << result; |
| 164 return result; |
| 165 } |
| 166 |
| 167 WSAQUERYSET query_set = {0}; |
| 168 query_set.dwSize = sizeof(WSAQUERYSET); |
| 169 query_set.dwNameSpace = NS_NLA; |
| 170 // Initiate a client query to iterate through the |
| 171 // currently connected networks. |
| 172 if (0 != ::WSALookupServiceBegin(&query_set, LUP_RETURN_ALL, |
| 173 &ws_handle_)) { |
| 174 result = ::WSAGetLastError(); |
| 175 LOG_F(LS_INFO) << "WSACleanup 2"; |
| 176 ::WSACleanup(); |
| 177 ASSERT(ws_handle_ == NULL); |
| 178 ws_handle_ = NULL; |
| 179 return result; |
| 180 } |
| 181 return 0; |
| 182 } |
| 183 talk_base::CriticalSection crit_sect_; |
| 184 HANDLE ws_handle_; |
| 185 scoped_event event_handle_; |
| 186 DISALLOW_COPY_AND_ASSIGN(PlatformNetworkInfo); |
| 187 }; |
| 188 |
| 189 class AsyncNetworkAliveWin32 : public AsyncNetworkAlive { |
| 190 public: |
| 191 AsyncNetworkAliveWin32() { |
| 192 } |
| 193 |
| 194 virtual ~AsyncNetworkAliveWin32() { |
| 195 if (network_info_) { |
| 196 delete network_info_; |
| 197 network_info_ = NULL; |
| 198 } |
| 199 } |
| 200 |
| 201 protected: |
| 202 // SignalThread Interface |
| 203 virtual void DoWork() { |
| 204 if (!network_info_) { |
| 205 network_info_ = new PlatformNetworkInfo(); |
| 206 } else { |
| 207 // Since network_info is set, it means that |
| 208 // we are suppose to wait for network state changes. |
| 209 if (!network_info_->WaitForChange()) { |
| 210 // The wait was aborted so we must be shutting down. |
| 211 alive_ = false; |
| 212 error_ = true; |
| 213 return; |
| 214 } |
| 215 } |
| 216 alive_ = network_info_->IsAlive(&error_); |
| 217 } |
| 218 |
| 219 virtual void OnWorkStop() { |
| 220 if (network_info_) { |
| 221 network_info_->Close(); |
| 222 } |
| 223 } |
| 224 |
| 225 private: |
| 226 DISALLOW_COPY_AND_ASSIGN(AsyncNetworkAliveWin32); |
| 227 }; |
| 228 |
| 229 AsyncNetworkAlive* AsyncNetworkAlive::Create() { |
| 230 return new AsyncNetworkAliveWin32(); |
| 231 } |
| 232 |
| 233 } // namespace notifier |
OLD | NEW |