summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/libgtk2ui/g_object_destructor_filo.cc
blob: 54ed7eec94741ae1f2a2240ed196df2ba2a47e13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// 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 "chrome/browser/ui/libgtk2ui/g_object_destructor_filo.h"

#include <glib-object.h>

#include "base/logging.h"
#include "base/memory/singleton.h"

namespace libgtk2ui {

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 base::Singleton<GObjectDestructorFILO>::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);
}

}  // namespace libgtk2ui