Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(165)

Side by Side Diff: src/service_manager.cc

Issue 5333003: cashew: do not delete DBus::ObjectProxy objects from D-Bus callbacks (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/cashew.git@master
Patch Set: Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« src/service.cc ('K') | « src/service_manager.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« src/service.cc ('K') | « src/service_manager.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698