// Copyright (c) 2012 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 "ui/base/gtk/g_object_destructor_filo.h" #include #include "base/logging.h" #include "base/memory/singleton.h" namespace ui { GObjectDestructorFILO::GObjectDestructorFILO() { } GObjectDestructorFILO::~GObjectDestructorFILO() { // Probably CHECK(handler_map_.empty()) would look natural here. But // some tests (some views_unittests) violate this assertion. } // static GObjectDestructorFILO* GObjectDestructorFILO::GetInstance() { return Singleton::get(); } void GObjectDestructorFILO::Connect( GObject* object, DestructorHook callback, void* context) { const Hook hook(object, callback, context); HandlerMap::iterator iter = handler_map_.find(object); if (iter == handler_map_.end()) { g_object_weak_ref(object, WeakNotifyThunk, this); handler_map_[object].push_front(hook); } else { iter->second.push_front(hook); } } void GObjectDestructorFILO::Disconnect( GObject* object, DestructorHook callback, void* context) { HandlerMap::iterator iter = handler_map_.find(object); if (iter == handler_map_.end()) { LOG(DFATAL) << "Unable to disconnect destructor hook for object " << object << ": hook not found (" << callback << ", " << context << ")."; return; } HandlerList& dtors = iter->second; if (dtors.empty()) { LOG(DFATAL) << "Destructor list is empty for specified object " << object << " Maybe it is being executed?"; return; } if (!dtors.front().equal(object, callback, context)) { // Reenable this warning once this bug is fixed: // http://code.google.com/p/chromium/issues/detail?id=85603 DVLOG(1) << "Destructors should be unregistered the reverse order they " << "were registered. But for object " << object << " " << "deleted hook is "<< context << ", the last queued hook is " << dtors.front().context; } for (HandlerList::iterator i = dtors.begin(); i != dtors.end(); ++i) { if (i->equal(object, callback, context)) { dtors.erase(i); break; } } if (dtors.empty()) { g_object_weak_unref(object, WeakNotifyThunk, this); handler_map_.erase(iter); } } void GObjectDestructorFILO::WeakNotify(GObject* where_the_object_was) { HandlerMap::iterator iter = handler_map_.find(where_the_object_was); DCHECK(iter != handler_map_.end()); DCHECK(!iter->second.empty()); // Save destructor list for given object into local copy to avoid reentrancy // problem: if callee wants to modify the caller list. HandlerList dtors; iter->second.swap(dtors); handler_map_.erase(iter); // Execute hooks in local list in FILO order. for (HandlerList::iterator i = dtors.begin(); i != dtors.end(); ++i) i->callback(i->context, where_the_object_was); } } // napespace ui