safe_callback.cpp

Go to the documentation of this file.
00001 #ifndef SAFE_CALLBACK_IMPLEMENTATION_FILE
00002 #define SAFE_CALLBACK_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : safe_callback                                                     *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 2001-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "safe_callback.h"
00019 
00020 #include <basis/guards.h>
00021 #include <basis/istring.h>
00022 #include <basis/mutex.h>
00023 #include <data_struct/byte_hasher.h>
00024 #include <data_struct/hash_table.cpp>
00025 #include <data_struct/static_memory_gremlin.h>
00026 
00028 
00029 callback_data_block::~callback_data_block() {}
00030 
00032 
00033 class live_object_info
00034 {
00035 public:
00036   int _references;  // the number of times it's been added to the list.
00037 
00038   live_object_info() : _references(1) {}
00039 };
00040 
00042 
00043 static bool _live_objects_are_gone = false;
00044   // flags when the global_live_objects singleton winks out of existence.  this
00045   // should prevent ordering conflicts during the static destructions.
00046 
00047 class global_live_objects : public virtual object_base
00048 {
00049 public:
00050   global_live_objects() : _objects(rotating_byte_hasher(), 12) {}
00051     // note that we have about a 2 billion callback object limit currently.
00052 
00053   ~global_live_objects() { _live_objects_are_gone = true; }
00054 
00055   IMPLEMENT_CLASS_NAME("global_live_objects");
00056 
00057   // returns true if the "object" is listed as valid.
00058   bool listed(void *object) {
00059     auto_synchronizer l(_lock);
00060     live_object_info *loi = NIL;
00061     return _objects.find(object, loi);
00062   }
00063 
00064   // adds the "object" to the list, or if it's already there, ups the refcount.
00065   void add(void *object) {
00066     auto_synchronizer l(_lock);
00067     live_object_info *loi = NIL;
00068     if (!_objects.find(object, loi)) {
00069       // this is a new item.
00070       _objects.add(object, new live_object_info);
00071       return;
00072     }
00073     // this item already exists.
00074     loi->_references++;
00075   }
00076 
00077   // reduces the refcount on the "object" and removes it if there are zero
00078   // references.
00079   void remove(void *object) {
00080     auto_synchronizer l(_lock);
00081     live_object_info *loi = NIL;
00082     if (!_objects.find(object, loi)) {
00083       // this item doesn't exist???  bad usage has occurred..
00084       return;
00085     }
00086     // patch its reference count.
00087     loi->_references--;
00088     if (!loi->_references) {
00089       // ooh, it croaked.  get rid of it now.
00090       _objects.zap(object);
00091     }
00092   }
00093 
00094 private:
00095   mutex _lock;  // protects our list.
00096   hash_table<void *, live_object_info> _objects;
00097     // the valid objects are listed here.
00098 };
00099 
00101 
00102 safe_callback::safe_callback()
00103 : _decoupled(false),
00104   _callback_lock(new mutex)
00105 { begin_availability(); }
00106 
00107 safe_callback::~safe_callback()
00108 {
00109   if (!_decoupled)
00110     non_continuable_error(class_name(), "destructor",
00111         "the derived safe_callback has not called end_availability() yet.\r\n"
00112         "this violates caveat two of safe_callback (see header).");
00113   WHACK(_callback_lock);
00114 }
00115 
00116 SAFE_STATIC(global_live_objects, safe_callback::_invocables, )
00117 
00118 void safe_callback::begin_availability()
00119 {
00120   // we don't lock the mutex here because there'd better not already be
00121   // a call to the callback function that happens while the safe_callback
00122   // object is still being constructed...!
00123 
00124   _decoupled = false;  // set to be sure we know we ARE hooked in.
00125   if (_live_objects_are_gone) return;  // hosed.
00126   _invocables().add(this);  // list this object as valid.
00127 }
00128 
00129 void safe_callback::end_availability()
00130 {
00131   if (_decoupled) return;  // never unhook any one safe_callback object twice.
00132   if (_live_objects_are_gone) {
00133     // nothing to unlist from.
00134     _decoupled = true;
00135     return;
00136   }
00137   _callback_lock->lock();  // protect access to this object.
00138   _invocables().remove(this);  // unlist this object.
00139   _decoupled = true;  // we are now out of the action.
00140   _callback_lock->unlock();  // release lock again.
00141   // we shoot the lock here so that people hear about it immediately.  they
00142   // will then be released from their pending but failed callback invocations.
00143   WHACK(_callback_lock);
00144 }
00145 
00146 bool safe_callback::invoke_callback(callback_data_block &new_data)
00147 {
00148   auto_synchronizer l(*_callback_lock);
00149     // we now have the lock.
00150   if (!_invocables().listed(this)) return false;
00151     // this object is no longer valid, so we must not touch it.
00152   if (_decoupled) return false;
00153     // object is partially decoupled already somehow.  perhaps this instance
00154     // has already been connected but another hook-up exists at some place
00155     // else in the derivation hierarchy.  they'd better be careful to shut
00156     // down all the safe_callbacks before proceeding to destroy...
00157     // if they do that, they're fine, since the shutdown of any owned data
00158     // members would be postponed until after all the callbacks had been
00159     // removed.
00160   real_callback(new_data);
00161   return true;
00162 }
00163 
00164 
00165 #endif //SAFE_CALLBACK_IMPLEMENTATION_FILE
00166 

Generated on Fri Nov 21 04:29:50 2008 for HOOPLE Libraries by  doxygen 1.5.1