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>
24 #include <structures/byte_hasher.h>
25 #include <structures/hash_table.h>
27 
28 using namespace basis;
29 using namespace loggers;
30 using namespace structures;
31 
32 namespace processes {
33 
35 
36 callback_data_block::~callback_data_block() {}
37 
39 
40 class live_object_info
41 {
42 public:
43  int _references; // the number of times it's been added to the list.
44 
45  live_object_info() : _references(1) {}
46 };
47 
49 
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.
53 
54 class global_live_objects : public virtual root_object
55 {
56 public:
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 
101 private:
102  mutex _lock; // protects our list.
104  // the valid objects are listed here.
105 };
106 
108 
109 safe_callback::safe_callback()
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 
123 SAFE_STATIC(global_live_objects, safe_callback::_invocables, )
124 
125 void 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:95
void unlock()
Gives up the possession of the mutex.
Definition: mutex.cpp:107
a simple place-holder that anonymizes the type passed to the callback.
A reasonably easy way to make callbacks safe from shutdown ordering issues.
Definition: safe_callback.h:68
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.
global_live_objects & _invocables()
provides access to the program-wide list of healthy callback objects.
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:45
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.