Home | History | Annotate | Download | only in gtk
      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 "ui/base/gtk/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 ui {
     13 
     14 GObjectDestructorFILO::GObjectDestructorFILO() {
     15 }
     16 
     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
     23 GObjectDestructorFILO* GObjectDestructorFILO::GetInstance() {
     24   return Singleton<GObjectDestructorFILO>::get();
     25 }
     26 
     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 
     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 
     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 }  // napespace ui
     90