1 // Copyright (c) 2012 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/ui/libgtk2ui/g_object_destructor_filo.h"
6
7 #include <glib-object.h>
8
9 #include "base/logging.h"
10 #include "base/memory/singleton.h"
11
12 namespace libgtk2ui {
13
GObjectDestructorFILO()14 GObjectDestructorFILO::GObjectDestructorFILO() {
15 }
16
~GObjectDestructorFILO()17 GObjectDestructorFILO::~GObjectDestructorFILO() {
18 // Probably CHECK(handler_map_.empty()) would look natural here. But
19 // some tests (some views_unittests) violate this assertion.
20 }
21
22 // static
GetInstance()23 GObjectDestructorFILO* GObjectDestructorFILO::GetInstance() {
24 return Singleton<GObjectDestructorFILO>::get();
25 }
26
Connect(GObject * object,DestructorHook callback,void * context)27 void GObjectDestructorFILO::Connect(
28 GObject* object, DestructorHook callback, void* context) {
29 const Hook hook(object, callback, context);
30 HandlerMap::iterator iter = handler_map_.find(object);
31 if (iter == handler_map_.end()) {
32 g_object_weak_ref(object, WeakNotifyThunk, this);
33 handler_map_[object].push_front(hook);
34 } else {
35 iter->second.push_front(hook);
36 }
37 }
38
Disconnect(GObject * object,DestructorHook callback,void * context)39 void GObjectDestructorFILO::Disconnect(
40 GObject* object, DestructorHook callback, void* context) {
41 HandlerMap::iterator iter = handler_map_.find(object);
42 if (iter == handler_map_.end()) {
43 LOG(DFATAL) << "Unable to disconnect destructor hook for object " << object
44 << ": hook not found (" << callback << ", " << context << ").";
45 return;
46 }
47 HandlerList& dtors = iter->second;
48 if (dtors.empty()) {
49 LOG(DFATAL) << "Destructor list is empty for specified object " << object
50 << " Maybe it is being executed?";
51 return;
52 }
53 if (!dtors.front().equal(object, callback, context)) {
54 // Reenable this warning once this bug is fixed:
55 // http://code.google.com/p/chromium/issues/detail?id=85603
56 DVLOG(1) << "Destructors should be unregistered the reverse order they "
57 << "were registered. But for object " << object << " "
58 << "deleted hook is "<< context << ", the last queued hook is "
59 << dtors.front().context;
60 }
61 for (HandlerList::iterator i = dtors.begin(); i != dtors.end(); ++i) {
62 if (i->equal(object, callback, context)) {
63 dtors.erase(i);
64 break;
65 }
66 }
67 if (dtors.empty()) {
68 g_object_weak_unref(object, WeakNotifyThunk, this);
69 handler_map_.erase(iter);
70 }
71 }
72
WeakNotify(GObject * where_the_object_was)73 void GObjectDestructorFILO::WeakNotify(GObject* where_the_object_was) {
74 HandlerMap::iterator iter = handler_map_.find(where_the_object_was);
75 DCHECK(iter != handler_map_.end());
76 DCHECK(!iter->second.empty());
77
78 // Save destructor list for given object into local copy to avoid reentrancy
79 // problem: if callee wants to modify the caller list.
80 HandlerList dtors;
81 iter->second.swap(dtors);
82 handler_map_.erase(iter);
83
84 // Execute hooks in local list in FILO order.
85 for (HandlerList::iterator i = dtors.begin(); i != dtors.end(); ++i)
86 i->callback(i->context, where_the_object_was);
87 }
88
89 } // namespace libgtk2ui
90