Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/service_manager.h" | 5 #include "src/service_manager.h" |
| 6 | 6 |
| 7 #include <glog/logging.h> | 7 #include <glog/logging.h> |
| 8 | 8 |
| 9 #include "src/cashew_server.h" | 9 #include "src/cashew_server.h" |
| 10 #include "src/service.h" | 10 #include "src/service.h" |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 26 static const char *kFlimflamStateOnline = "online"; | 26 static const char *kFlimflamStateOnline = "online"; |
| 27 | 27 |
| 28 // Flimflam service types | 28 // Flimflam service types |
| 29 static const char *kTypeCellular = "cellular"; | 29 static const char *kTypeCellular = "cellular"; |
| 30 | 30 |
| 31 // GetFlimflamProperties retry interval | 31 // GetFlimflamProperties retry interval |
| 32 static const guint kSecondsPerMinute = 60; | 32 static const guint kSecondsPerMinute = 60; |
| 33 static const guint kGetFlimflamPropertiesIntervalSeconds = | 33 static const guint kGetFlimflamPropertiesIntervalSeconds = |
| 34 1 * kSecondsPerMinute; | 34 1 * kSecondsPerMinute; |
| 35 | 35 |
| 36 ServiceManager::ServiceManager(DBus::Connection& connection) // NOLINT | 36 ServiceManager::ServiceManager(DBus::Connection& connection, // NOLINT |
| 37 GMainLoop * const main_loop) | |
| 37 : DBus::ObjectProxy(connection, kFlimflamManagerPath, | 38 : DBus::ObjectProxy(connection, kFlimflamManagerPath, |
| 38 kFlimflamManagerName), | 39 kFlimflamManagerName), |
| 39 connection_(connection), cashew_server_(NULL), | 40 connection_(connection), main_loop_(CHECK_NOTNULL(main_loop)), |
| 40 default_technology_(Service::kTypeUnknown), | 41 cashew_server_(NULL), default_technology_(Service::kTypeUnknown), |
| 41 default_cellular_service_(NULL), | 42 default_cellular_service_(NULL), |
| 42 connectivity_state_(kConnectivityStateUnknown), | 43 connectivity_state_(kConnectivityStateUnknown), |
| 43 get_properties_source_id_(0), retrying_get_properties_(false) { | 44 get_properties_source_id_(0), retrying_get_properties_(false), |
| 45 deferred_service_deletion_source_id_(0) { | |
| 44 // schedule a GetProperties() call to Flimflam to init our state | 46 // schedule a GetProperties() call to Flimflam to init our state |
| 45 // we'll keep trying periodically until we succeed | 47 // we'll keep trying periodically until we succeed |
| 46 // we'll subsequently update this state by monitoring PropertyChanged signals | 48 // we'll subsequently update this state by monitoring PropertyChanged signals |
| 47 get_properties_source_id_ = | 49 get_properties_source_id_ = |
| 48 g_idle_add(StaticGetFlimflamPropertiesCallback, this); | 50 g_idle_add(StaticGetFlimflamPropertiesCallback, this); |
| 49 if (get_properties_source_id_ == 0) { | 51 if (get_properties_source_id_ == 0) { |
| 50 LOG(ERROR) << "ctor: g_idle_add failed"; | 52 LOG(ERROR) << "ctor: g_idle_add failed"; |
| 51 } | 53 } |
| 52 } | 54 } |
| 53 | 55 |
| 54 ServiceManager::~ServiceManager() { | 56 ServiceManager::~ServiceManager() { |
| 57 DCHECK(!g_main_loop_is_running(main_loop_)); | |
| 55 ClearDefaultCellularService(); | 58 ClearDefaultCellularService(); |
| 56 DeleteServices(&services_); | 59 DeleteServicesWhenPossible(&services_); |
| 60 DeletePendingServices(); | |
| 57 if (get_properties_source_id_ != 0 && | 61 if (get_properties_source_id_ != 0 && |
| 58 !g_source_remove(get_properties_source_id_)) { | 62 !g_source_remove(get_properties_source_id_)) { |
| 59 DLOG(WARNING) << "dtor: g_source_remove failed"; | 63 DLOG(WARNING) << "dtor: g_source_remove failed for source id " |
| 64 << get_properties_source_id_; | |
| 65 } | |
| 66 if (deferred_service_deletion_source_id_ != 0 && | |
| 67 !g_source_remove(deferred_service_deletion_source_id_)) { | |
| 68 DLOG(WARNING) << "dtor: g_source_remove failed for source id " | |
| 69 << deferred_service_deletion_source_id_; | |
| 60 } | 70 } |
| 61 } | 71 } |
| 62 | 72 |
| 63 const Service* ServiceManager::GetService(const std::string& service_path) | 73 const Service* ServiceManager::GetService(const std::string& service_path) |
| 64 const { | 74 const { |
| 65 ServiceMap::const_iterator it = services_.find(service_path); | 75 ServiceMap::const_iterator it = services_.find(service_path); |
| 66 if (it == services_.end()) { | 76 if (it == services_.end()) { |
| 67 return NULL; | 77 return NULL; |
| 68 } | 78 } |
| 69 DCHECK(it->second != NULL); | 79 DCHECK(it->second != NULL); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 162 } | 172 } |
| 163 | 173 |
| 164 bool ServiceManager::RetryingGetProperties() const { | 174 bool ServiceManager::RetryingGetProperties() const { |
| 165 return retrying_get_properties_; | 175 return retrying_get_properties_; |
| 166 } | 176 } |
| 167 | 177 |
| 168 void ServiceManager::OnRetryingGetProperties(bool retrying) { | 178 void ServiceManager::OnRetryingGetProperties(bool retrying) { |
| 169 retrying_get_properties_ = retrying; | 179 retrying_get_properties_ = retrying; |
| 170 } | 180 } |
| 171 | 181 |
| 182 guint ServiceManager::GetDeferredServiceDeletionSourceId() const { | |
| 183 return deferred_service_deletion_source_id_; | |
| 184 } | |
| 185 | |
| 186 void ServiceManager::SetDeferredServiceDeletionSourceId(guint source_id) { | |
| 187 deferred_service_deletion_source_id_ = source_id; | |
| 188 } | |
| 189 | |
| 172 // Private methods | 190 // Private methods |
| 173 | 191 |
| 174 void ServiceManager::DeleteServices(ServiceMap *service_map) { | 192 void ServiceManager::DeleteServicesWhenPossible(ServiceMap *service_map) { |
| 175 DCHECK(service_map != NULL); | 193 DCHECK(service_map != NULL); |
| 176 while (!service_map->empty()) { | 194 while (!service_map->empty()) { |
| 177 const std::string& path = | 195 const std::string& path = |
| 178 static_cast<std::string>(service_map->begin()->first); | 196 static_cast<std::string>(service_map->begin()->first); |
| 179 DLOG(INFO) << "DeleteServices: deleting service: " << path; | 197 DLOG(INFO) << "DeleteServicesWhenPossible: considering deleting service: " |
| 198 << path; | |
| 180 Service *service = static_cast<Service *>(service_map->begin()->second); | 199 Service *service = static_cast<Service *>(service_map->begin()->second); |
| 200 DCHECK(*service_map == services_ || GetService(service->GetPath()) == NULL); | |
| 201 DeleteServiceOrScheduleForLaterDeletion(service); | |
| 202 service_map->erase(service_map->begin()); | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 void ServiceManager::DeletePendingServices() { | |
|
Daniel Kurtz
2010/11/24 02:33:47
Why not just services_pending_deletion_.clear() ?
Vince Laviano
2010/11/24 04:48:34
The elements are pointers to Services. This sugges
| |
| 207 while (!services_pending_deletion_.empty()) { | |
| 208 Service *service = *(services_pending_deletion_.begin()); | |
| 209 DCHECK(service != NULL); | |
| 210 DLOG(INFO) << "DeletePendingServices: deleting service: " | |
| 211 << service->GetPath(); | |
| 212 DCHECK(service != default_cellular_service_); | |
| 213 delete service; | |
| 214 services_pending_deletion_.erase(services_pending_deletion_.begin()); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 void ServiceManager::DeleteServiceOrScheduleForLaterDeletion(Service *service) { | |
| 181 DCHECK(service != NULL); | 219 DCHECK(service != NULL); |
| 182 DCHECK(service != default_cellular_service_); | 220 DCHECK(service != default_cellular_service_); |
| 183 DCHECK(*service_map == services_ || GetService(service->GetPath()) == NULL); | 221 // if the main loop is not running, then we can delete the service |
| 184 delete service; | 222 // immediately without fear of sending a D-Bus msg from a D-Bus callback (by |
| 185 service_map->erase(service_map->begin()); | 223 // virtue of calling the DBus::ObjectProxy dtor) and triggering a dbus-c++ |
| 186 } | 224 // deadlock. |
| 225 // | |
| 226 // TODO(vlaviano): There is a race here if we receive a signal and call | |
| 227 // g_main_loop_quit() from the signal handler. This may not be much of an | |
| 228 // issue since we are likely receiving the signal from upstart and it will | |
| 229 // proceed to kill -9 us if we become unresponsive and fail to exit. If we | |
| 230 // care, we could have the sighandler g_idle_add a callback that quits the | |
| 231 // main loop instead of quitting it directly. | |
| 232 if (!g_main_loop_is_running(main_loop_)) { | |
|
Daniel Kurtz
2010/11/24 02:33:47
Break this into two methods:
1) always defers, and
Vince Laviano
2010/11/24 04:48:34
Done. (Good call.)
| |
| 233 DLOG(INFO) | |
| 234 << "DeleteServiceOrScheduleForLaterDeletion: deleting service: " | |
| 235 << service->GetPath(); | |
| 236 delete service; | |
| 237 return; | |
| 238 } | |
| 239 // If the main loop is running, then we might be inside of a D-Bus callback. | |
| 240 // We add the service to our |services_pending_deletion_| list and schedule | |
| 241 // a glib callback to delete these services from the main loop if one is not | |
| 242 // already scheduled. | |
| 243 DLOG(INFO) << "DeleteServiceOrScheduleForLaterDeletion: scheduling service " | |
| 244 << "for later deletion: " << service->GetPath(); | |
| 245 services_pending_deletion_.push_back(service); | |
| 246 if (deferred_service_deletion_source_id_ != 0) { | |
| 247 DLOG(INFO) << "DeleteServiceOrScheduleForLaterDeletion: " | |
| 248 << "deferred service deletion callback already scheduled"; | |
| 249 return; | |
| 250 } | |
| 251 deferred_service_deletion_source_id_ = | |
| 252 g_idle_add(StaticDeletePendingServicesCallback, this); | |
| 253 if (deferred_service_deletion_source_id_ == 0) { | |
| 254 LOG(ERROR) | |
| 255 << "DeleteServiceOrScheduleForLaterDeletion: g_idle_add failed"; | |
| 256 return; | |
| 257 } | |
| 258 DLOG(INFO) << "DeleteServiceOrScheduleForLaterDeletion: " | |
| 259 << "scheduled deferred service deletion callback"; | |
| 260 } | |
| 261 | |
| 262 // static | |
| 263 gboolean ServiceManager::StaticDeletePendingServicesCallback(gpointer data) { | |
| 264 ServiceManager *service_manager = reinterpret_cast<ServiceManager*>(data); | |
| 265 CHECK_NOTNULL(service_manager); | |
| 266 DCHECK_NE(service_manager->GetDeferredServiceDeletionSourceId(), 0); | |
| 267 service_manager->DeletePendingServices(); | |
| 268 // we don't want to be run again automatically | |
| 269 // DeleteServiceOrScheduleForLaterDeletion will schedule us as needed | |
| 270 service_manager->SetDeferredServiceDeletionSourceId(0); | |
| 271 return FALSE; | |
| 187 } | 272 } |
| 188 | 273 |
| 189 // static | 274 // static |
| 190 gboolean ServiceManager::StaticGetFlimflamPropertiesCallback(gpointer data) { | 275 gboolean ServiceManager::StaticGetFlimflamPropertiesCallback(gpointer data) { |
| 191 ServiceManager *service_manager = reinterpret_cast<ServiceManager*>(data); | 276 ServiceManager *service_manager = reinterpret_cast<ServiceManager*>(data); |
| 192 CHECK_NOTNULL(service_manager); | 277 CHECK_NOTNULL(service_manager); |
| 193 DCHECK_NE(service_manager->GetGetPropertiesSourceId(), 0); | 278 DCHECK_NE(service_manager->GetGetPropertiesSourceId(), 0); |
| 194 if (!service_manager->GetFlimflamProperties()) { | 279 if (!service_manager->GetFlimflamProperties()) { |
| 195 // call failed, so try again later | 280 // call failed, so try again later |
| 196 DLOG(WARNING) << "StaticGetFlimflamPropertiesCallback: " | 281 DLOG(WARNING) << "StaticGetFlimflamPropertiesCallback: " |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 312 | 397 |
| 313 ServiceMap::iterator old_it = old_services.find(path); | 398 ServiceMap::iterator old_it = old_services.find(path); |
| 314 if (old_it != old_services.end()) { | 399 if (old_it != old_services.end()) { |
| 315 // service is old: move it from old_services to services_ | 400 // service is old: move it from old_services to services_ |
| 316 Service *service = static_cast<Service *>(old_it->second); | 401 Service *service = static_cast<Service *>(old_it->second); |
| 317 DCHECK(service != NULL); | 402 DCHECK(service != NULL); |
| 318 old_services.erase(old_it); | 403 old_services.erase(old_it); |
| 319 services_[path] = service; | 404 services_[path] = service; |
| 320 } else { | 405 } else { |
| 321 // service is new: create a Service obj for it and add it to services_ | 406 // service is new: create a Service obj for it and add it to services_ |
| 407 // TODO(vlaviano): If a service went away and came back immediately, there | |
| 408 // might still be a corresponding service object pending deletion. We | |
| 409 // could rescue it rather than allocating a new object here. | |
| 322 OnNewService(path); | 410 OnNewService(path); |
| 323 } | 411 } |
| 324 } | 412 } |
| 325 | 413 |
| 326 // clear default service if it failed to appear in the update and we're | 414 // clear default service if it failed to appear in the update and we're |
| 327 // about to delete it | 415 // about to delete it |
| 328 if (default_cellular_service_ != NULL) { | 416 if (default_cellular_service_ != NULL) { |
| 329 ServiceMap::const_iterator it = | 417 ServiceMap::const_iterator it = |
| 330 old_services.find(default_cellular_service_->GetPath()); | 418 old_services.find(default_cellular_service_->GetPath()); |
| 331 if (it != old_services.end()) { | 419 if (it != old_services.end()) { |
| 332 ClearDefaultCellularService(); | 420 ClearDefaultCellularService(); |
| 333 } | 421 } |
| 334 } | 422 } |
| 335 | 423 |
| 336 // services left in old_services failed to appear in the update: delete them | 424 // services left in old_services failed to appear in the update: delete them |
| 337 DeleteServices(&old_services); | 425 DeleteServicesWhenPossible(&old_services); |
| 338 | 426 |
| 339 // Flimflam always places the default service first in its list of services, | 427 // Flimflam always places the default service first in its list of services, |
| 340 // so we'll take a look at the first element in the paths list and see if the | 428 // so we'll take a look at the first element in the paths list and see if the |
| 341 // default service has changed. Note that |paths| can be empty. We still want | 429 // default service has changed. Note that |paths| can be empty. We still want |
| 342 // to know about this, since it means that there is no default service. | 430 // to know about this, since it means that there is no default service. |
| 343 const DBus::Path *default_service_path = NULL; | 431 const DBus::Path *default_service_path = NULL; |
| 344 if (!paths.empty()) { | 432 if (!paths.empty()) { |
| 345 default_service_path = &*paths.begin(); | 433 default_service_path = &*paths.begin(); |
| 346 } | 434 } |
| 347 OnDefaultServiceUpdate(default_service_path); | 435 OnDefaultServiceUpdate(default_service_path); |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 431 // notify our child services | 519 // notify our child services |
| 432 ServiceMap::iterator it; | 520 ServiceMap::iterator it; |
| 433 for (it = services_.begin(); it != services_.end(); ++it) { | 521 for (it = services_.begin(); it != services_.end(); ++it) { |
| 434 Service *service = static_cast<Service*>(it->second); | 522 Service *service = static_cast<Service*>(it->second); |
| 435 DCHECK(service != NULL); | 523 DCHECK(service != NULL); |
| 436 service->OnFlimflamOffline(); | 524 service->OnFlimflamOffline(); |
| 437 } | 525 } |
| 438 } | 526 } |
| 439 | 527 |
| 440 } // namespace cashew | 528 } // namespace cashew |
| OLD | NEW |