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
12namespace libgtk2ui {
13
14GObjectDestructorFILO::GObjectDestructorFILO() {
15}
16
17GObjectDestructorFILO::~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
23GObjectDestructorFILO* GObjectDestructorFILO::GetInstance() {
24  return Singleton<GObjectDestructorFILO>::get();
25}
26
27void 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
39void 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
73void 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