feisty meow concerns codebase 2.140
safe_callback.cpp
Go to the documentation of this file.
1
2
3
4/*****************************************************************************\
5* *
6* Name : safe_callback *
7* Author : Chris Koeritz *
8* *
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\*****************************************************************************/
17
18#include "safe_callback.h"
19
20#include <basis/guards.h>
21#include <basis/astring.h>
22#include <basis/mutex.h>
27
28using namespace basis;
29using namespace loggers;
30using namespace structures;
31
32namespace processes {
33
35
37
39
40class live_object_info
41{
42public:
43 int _references; // the number of times it's been added to the list.
44
45 live_object_info() : _references(1) {}
46};
47
49
50static 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.
53
54class global_live_objects : public virtual root_object
55{
56public:
57 global_live_objects() : _objects(rotating_byte_hasher(), 12) {}
58 // note that we have about a 2 billion callback object limit currently.
59
60 ~global_live_objects() { _live_objects_are_gone = true; }
61
62 DEFINE_CLASS_NAME("global_live_objects");
63
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);
69 }
70
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);
78 return;
79 }
80 // this item already exists.
81 loi->_references++;
82 }
83
84 // reduces the refcount on the "object" and removes it if there are zero
85 // references.
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..
91 return;
92 }
93 // patch its reference count.
94 loi->_references--;
95 if (!loi->_references) {
96 // ooh, it croaked. get rid of it now.
97 _objects.zap(object);
98 }
99 }
100
101private:
102 mutex _lock; // protects our list.
104 // the valid objects are listed here.
105};
106
108
110: _decoupled(false),
111 _callback_lock(new mutex)
112{ begin_availability(); }
113
115{
116 if (!_decoupled)
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);
121}
122
123SAFE_STATIC(global_live_objects, safe_callback::_invocables, )
124
125void safe_callback::begin_availability()
126{
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...!
130
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.
134}
135
137{
138 if (_decoupled) return; // never unhook any one safe_callback object twice.
139 if (_live_objects_are_gone) {
140 // nothing to unlist from.
141 _decoupled = true;
142 return;
143 }
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);
151}
152
154{
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
166 // removed.
167 real_callback(new_data);
168 return true;
169}
170
171} //namespace.
172
173
174
auto_synchronizer simplifies concurrent code by automatically unlocking.
Definition mutex.h:113
void lock()
Clamps down on the mutex, if possible.
Definition mutex.cpp:101
void unlock()
Gives up the possession of the mutex.
Definition mutex.cpp:113
a simple place-holder that anonymizes the type passed to the callback.
A reasonably easy way to make callbacks safe from shutdown ordering issues.
global_live_objects & _invocables()
provides access to the program-wide list of healthy callback objects.
bool invoke_callback(callback_data_block &new_data)
this function is invoked by a user of the safe_callback derived object.
virtual void real_callback(callback_data_block &new_data)=0
derived classes implement this to provide their callback functionality.
void end_availability()
prepares to shut down this object.
virtual ~safe_callback()
actually destroys the object.
safe_callback()
construction signifies that the callback is now in operation.
Implements hashing into buckets for quick object access.
Definition hash_table.h:75
Implements a hashing algorithm based on the contents stored in an object.
Definition byte_hasher.h:36
#define non_continuable_error(c, f, i)
an extra piece of information used, if available, in bounds_halt below.
#define NULL_POINTER
The value representing a pointer to nothing.
Definition definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition enhance_cpp.h:42
The guards collection helps in testing preconditions and reporting errors.
Definition array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition functions.h:121
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
#define SAFE_STATIC(type, func_name, parms)
Statically defines a singleton object whose scope is the program's lifetime.