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