new fortune
[feisty_meow.git] / nucleus / library / processes / safe_callback.cpp
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>
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>
27
28 using namespace basis;
29 using namespace loggers;
30 using namespace structures;
31
32 namespace processes {
33
34 //////////////
35
36 callback_data_block::~callback_data_block() {}
37
38 //////////////
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
48 //////////////
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.
103   hash_table<void *, live_object_info> _objects;
104     // the valid objects are listed here.
105 };
106
107 //////////////
108
109 safe_callback::safe_callback()
110 : _decoupled(false),
111   _callback_lock(new mutex)
112 { begin_availability(); }
113
114 safe_callback::~safe_callback()
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
136 void safe_callback::end_availability()
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
153 bool safe_callback::invoke_callback(callback_data_block &new_data)
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