4 /*****************************************************************************\
6 * Name : safe_callback *
7 * Author : Chris Koeritz *
9 *******************************************************************************
10 * Copyright (c) 2001-$now By Author. This program is free software; you can *
11 * redistribute it and/or modify it under the terms of the GNU General Public *
12 * License as published by the Free Software Foundation; either version 2 of *
13 * the License or (at your option) any later version. This is online at: *
14 * http://www.fsf.org/copyleft/gpl.html *
15 * Please send any updates to: fred@gruntose.com *
16 \*****************************************************************************/
18 #include "safe_callback.h"
20 #include <basis/guards.h>
21 #include <basis/astring.h>
22 #include <basis/mutex.h>
23 #include <loggers/critical_events.h>
24 #include <structures/byte_hasher.h>
25 #include <structures/hash_table.h>
26 #include <structures/static_memory_gremlin.h>
28 using namespace basis;
29 using namespace loggers;
30 using namespace structures;
36 callback_data_block::~callback_data_block() {}
40 class live_object_info
43 int _references; // the number of times it's been added to the list.
45 live_object_info() : _references(1) {}
50 static bool _live_objects_are_gone = false;
51 // flags when the global_live_objects singleton winks out of existence. this
52 // should prevent ordering conflicts during the static destructions.
54 class global_live_objects : public virtual root_object
57 global_live_objects() : _objects(rotating_byte_hasher(), 12) {}
58 // note that we have about a 2 billion callback object limit currently.
60 ~global_live_objects() { _live_objects_are_gone = true; }
62 DEFINE_CLASS_NAME("global_live_objects");
64 // returns true if the "object" is listed as valid.
65 bool listed(void *object) {
66 auto_synchronizer l(_lock);
67 live_object_info *loi = NULL_POINTER;
68 return _objects.find(object, loi);
71 // adds the "object" to the list, or if it's already there, ups the refcount.
72 void add(void *object) {
73 auto_synchronizer l(_lock);
74 live_object_info *loi = NULL_POINTER;
75 if (!_objects.find(object, loi)) {
76 // this is a new item.
77 _objects.add(object, new live_object_info);
80 // this item already exists.
84 // reduces the refcount on the "object" and removes it if there are zero
86 void remove(void *object) {
87 auto_synchronizer l(_lock);
88 live_object_info *loi = NULL_POINTER;
89 if (!_objects.find(object, loi)) {
90 // this item doesn't exist??? bad usage has occurred..
93 // patch its reference count.
95 if (!loi->_references) {
96 // ooh, it croaked. get rid of it now.
102 mutex _lock; // protects our list.
103 hash_table<void *, live_object_info> _objects;
104 // the valid objects are listed here.
109 safe_callback::safe_callback()
111 _callback_lock(new mutex)
112 { begin_availability(); }
114 safe_callback::~safe_callback()
117 non_continuable_error(class_name(), "destructor",
118 "the derived safe_callback has not called end_availability() yet.\r\n"
119 "this violates caveat two of safe_callback (see header).");
120 WHACK(_callback_lock);
123 SAFE_STATIC(global_live_objects, safe_callback::_invocables, )
125 void safe_callback::begin_availability()
127 // we don't lock the mutex here because there'd better not already be
128 // a call to the callback function that happens while the safe_callback
129 // object is still being constructed...!
131 _decoupled = false; // set to be sure we know we ARE hooked in.
132 if (_live_objects_are_gone) return; // hosed.
133 _invocables().add(this); // list this object as valid.
136 void safe_callback::end_availability()
138 if (_decoupled) return; // never unhook any one safe_callback object twice.
139 if (_live_objects_are_gone) {
140 // nothing to unlist from.
144 _callback_lock->lock(); // protect access to this object.
145 _invocables().remove(this); // unlist this object.
146 _decoupled = true; // we are now out of the action.
147 _callback_lock->unlock(); // release lock again.
148 // we shoot the lock here so that people hear about it immediately. they
149 // will then be released from their pending but failed callback invocations.
150 WHACK(_callback_lock);
153 bool safe_callback::invoke_callback(callback_data_block &new_data)
155 auto_synchronizer l(*_callback_lock);
156 // we now have the lock.
157 if (!_invocables().listed(this)) return false;
158 // this object is no longer valid, so we must not touch it.
159 if (_decoupled) return false;
160 // object is partially decoupled already somehow. perhaps this instance
161 // has already been connected but another hook-up exists at some place
162 // else in the derivation hierarchy. they'd better be careful to shut
163 // down all the safe_callbacks before proceeding to destroy...
164 // if they do that, they're fine, since the shutdown of any owned data
165 // members would be postponed until after all the callbacks had been
167 real_callback(new_data);