├── Collector.cpp ├── Collector.hpp ├── LICENSE └── README.md /Collector.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Collector.cpp 3 | // 4 | // Created by Taylor Holliday on 6/14/13. 5 | // 6 | 7 | #include "Collector.hpp" 8 | 9 | Collector& Collector::GetInstance() { 10 | static Collector collector; 11 | return collector; 12 | } 13 | 14 | Collector::Collector() : _eventQueue(32000), _sequence(0), _graphChanged(false) { } 15 | 16 | void Collector::_PushEvent(const Event& e) { 17 | 18 | while(!_eventQueue.push(e)) { 19 | std::cout << "Warning: collector queue is full" << std::endl; 20 | } 21 | 22 | } 23 | 24 | void Collector::AddRoot(Collectable* node) { 25 | 26 | Event e; 27 | e.type = Event::AddRoot; 28 | e.a = node; 29 | 30 | _PushEvent(e); 31 | } 32 | 33 | void Collector::RemoveRoot(Collectable* node) { 34 | 35 | Event e; 36 | e.type = Event::RemoveRoot; 37 | e.a = node; 38 | 39 | _PushEvent(e); 40 | } 41 | 42 | void Collector::AddEdge(Collectable* a, Collectable* b) { 43 | 44 | Event e; 45 | e.type = Event::Connect; 46 | e.a = a; 47 | e.b = b; 48 | 49 | _PushEvent(e); 50 | } 51 | 52 | void Collector::RemoveEdge(Collectable* a, Collectable* b) { 53 | 54 | Event e; 55 | e.type = Event::Disconnect; 56 | e.a = a; 57 | e.b = b; 58 | 59 | _PushEvent(e); 60 | } 61 | 62 | void Collector::ProcessEvents() { 63 | 64 | boost::mutex::scoped_lock lock(_mutex); 65 | 66 | _ProcessEvents(); 67 | 68 | } 69 | 70 | void Collector::_ProcessEvents() { 71 | 72 | Event e; 73 | 74 | while(_eventQueue.pop(e)) { 75 | 76 | _graphChanged = true; 77 | 78 | switch (e.type) { 79 | case Event::AddRoot: { 80 | _nodes.insert(e.a); 81 | e.a->gcRootCount++; 82 | } 83 | break; 84 | case Event::RemoveRoot: { 85 | 86 | e.a->gcRootCount--; 87 | 88 | // Root count must be positive. 89 | assert(e.a->gcRootCount >= 0); 90 | } 91 | break; 92 | case Event::Connect: { 93 | 94 | e.a->gcConnections.push_back(e.b); 95 | } 96 | break; 97 | case Event::Disconnect: { 98 | 99 | std::vector& adj = e.a->gcConnections; 100 | 101 | auto iter = std::find(adj.begin(), adj.end(), e.b); 102 | 103 | // The connection must exist. 104 | assert(iter != e.a->gcConnections.end()); 105 | 106 | adj.erase(iter); 107 | } 108 | break; 109 | 110 | default: 111 | break; 112 | } 113 | } 114 | 115 | } 116 | 117 | void Collector::Collect() { 118 | 119 | boost::mutex::scoped_lock lock(_mutex); 120 | 121 | if(! _inGC.get()) { 122 | _inGC.reset(new bool); 123 | } 124 | 125 | *_inGC = true; 126 | 127 | _ProcessEvents(); 128 | 129 | if(_graphChanged) { 130 | 131 | _sequence++; 132 | 133 | std::vector nodeStack; 134 | 135 | // Traverse starting with roots. 136 | for (auto node : _nodes) { 137 | 138 | if(node->gcRootCount) { 139 | nodeStack.push_back(node); 140 | } 141 | } 142 | 143 | // Mark. 144 | while(! nodeStack.empty()) { 145 | 146 | Collectable* node = nodeStack.back(); 147 | nodeStack.pop_back(); 148 | 149 | if(node->gcSequence != _sequence) { 150 | node->gcSequence = _sequence; 151 | 152 | for(auto adj : node->gcConnections) { 153 | nodeStack.push_back(adj); 154 | } 155 | } 156 | } 157 | 158 | // Sweep. 159 | std::set newNodes; 160 | 161 | for(auto node : _nodes) { 162 | 163 | // Not visited. 164 | if (node->gcSequence != _sequence) { 165 | delete node; 166 | } else { 167 | newNodes.insert(node); 168 | } 169 | } 170 | 171 | _nodes.swap(newNodes); 172 | 173 | _graphChanged = false; 174 | 175 | } 176 | 177 | *_inGC = false; 178 | 179 | } 180 | 181 | -------------------------------------------------------------------------------- /Collector.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Collector.h 3 | // Dev 4 | // 5 | // Created by Taylor Holliday on 6/14/13. 6 | // 7 | 8 | #ifndef __Dev__Collector__ 9 | #define __Dev__Collector__ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // Derive from Collectable if you'd like an object 18 | // to be garbage collected. 19 | class Collectable { 20 | 21 | public: 22 | 23 | Collectable() : gcRootCount(0), gcSequence(0) { } 24 | virtual ~Collectable() { } 25 | 26 | private: 27 | 28 | friend class Collector; 29 | 30 | // Connections as seen by the garbage collector. 31 | std::vector gcConnections; 32 | 33 | // How many times is this node referenced as 34 | // a root. 35 | int gcRootCount; 36 | 37 | // Sequence number used to determine if 38 | // the Collectable has been visited in the 39 | // current round of GC. 40 | size_t gcSequence; 41 | 42 | }; 43 | 44 | // The garbage collector. A singleton. 45 | // 46 | // Collector is a mark-sweep garbage collector 47 | // that can be run concurrently in a background 48 | // thread. 49 | // 50 | class Collector { 51 | 52 | public: 53 | 54 | // Get the singleton instance. 55 | static Collector& GetInstance(); 56 | 57 | // Add a reference to a root collectable. 58 | void AddRoot(Collectable*); 59 | 60 | // Remove a reference to a root collectable. 61 | void RemoveRoot(Collectable*); 62 | 63 | // Notify the collector of a new reference 64 | // between Collectables. 65 | void AddEdge(Collectable*, Collectable*); 66 | 67 | // Remove a reference between Collectables. 68 | void RemoveEdge(Collectable*, Collectable*); 69 | 70 | // Process events coming from mutator threads. 71 | // Calling this is optional but if the mutator threads 72 | // are generating a lot of changes, yet you don't immedately 73 | // want to collect, this can be useful to call frequently. 74 | void ProcessEvents(); 75 | 76 | // Collect garbage. Make sure you only call 77 | // this from one thread at a time. 78 | void Collect(); 79 | 80 | // Are we in the garbage collector thread? 81 | bool InGC() { 82 | if(_inGC.get() == 0) { 83 | _inGC.reset(new bool(false)); 84 | } 85 | return *_inGC; 86 | } 87 | 88 | private: 89 | 90 | Collector(); 91 | 92 | // An event to update the collectors 93 | // own representation of the graph. 94 | struct Event { 95 | 96 | enum Type { 97 | AddRoot, 98 | RemoveRoot, 99 | Connect, 100 | Disconnect 101 | }; 102 | 103 | Type type; 104 | Collectable* a; 105 | Collectable* b; 106 | 107 | }; 108 | 109 | boost::lockfree::queue > _eventQueue; 110 | 111 | // All the nodes we've seen. 112 | std::set _nodes; 113 | 114 | void _PushEvent(const Event& e); 115 | 116 | void _ProcessEvents(); 117 | 118 | boost::thread_specific_ptr _inGC; 119 | 120 | size_t _sequence; 121 | 122 | // Has the graph changed since the 123 | // last time we collected? 124 | bool _graphChanged; 125 | 126 | // Don't allow more than one thread 127 | // doing collection. 128 | boost::mutex _mutex; 129 | 130 | }; 131 | 132 | // When passing references to Collectables 133 | // on the stack, always use RootPtr. 134 | template 135 | class RootPtr { 136 | 137 | public: 138 | 139 | RootPtr() : _ptr(0) { } 140 | 141 | explicit RootPtr(T* ptr) : _ptr(ptr) { 142 | _Retain(); 143 | } 144 | 145 | RootPtr(const RootPtr& other) : _ptr(other._ptr) { 146 | _Retain(); 147 | } 148 | 149 | template 150 | RootPtr(const RootPtr& other) : _ptr(other.Get()) { 151 | _Retain(); 152 | } 153 | 154 | ~RootPtr() { 155 | _Release(); 156 | } 157 | 158 | RootPtr& operator=(const RootPtr& other) { 159 | if(_ptr != other._ptr) { 160 | _Release(); 161 | _ptr = other._ptr; 162 | _Retain(); 163 | } 164 | return *this; 165 | } 166 | 167 | template 168 | RootPtr& operator=(const RootPtr& other) { 169 | if(_ptr != other._ptr) { 170 | _Release(); 171 | _ptr = other._ptr; 172 | _Retain(); 173 | } 174 | return *this; 175 | } 176 | 177 | T* Get() const { return _ptr; } 178 | 179 | T& operator*() { return *_ptr; } 180 | T* operator->() const { assert(_ptr); return _ptr; } 181 | 182 | operator T*() { return _ptr; } 183 | 184 | operator bool() const { return _ptr != 0; } 185 | 186 | bool operator==(const RootPtr& other) const { 187 | return _ptr == other._ptr; 188 | } 189 | 190 | bool operator!=(const RootPtr& other) const { 191 | return _ptr != other._ptr; 192 | } 193 | 194 | bool operator<(const RootPtr& other) const { 195 | return _ptr < other._ptr; 196 | } 197 | 198 | void _Retain() { 199 | if(_ptr) { 200 | Collector::GetInstance().AddRoot(_ptr); 201 | } 202 | } 203 | 204 | void _Release() { 205 | if(_ptr) { 206 | Collector::GetInstance().RemoveRoot(_ptr); 207 | } 208 | } 209 | 210 | private: 211 | 212 | T* _ptr; 213 | 214 | }; // class RootPtr 215 | 216 | template 217 | std::ostream& operator<<(std::ostream& out, const RootPtr& p) { 218 | return out << p._ptr; 219 | } 220 | 221 | // When passing references to Collectables 222 | // on the stack, always use RootPtr. 223 | template 224 | class EdgePtr { 225 | 226 | public: 227 | 228 | // Every EdgePtr must have an owner. 229 | EdgePtr(Collectable* owner) : _owner(owner), _ptr(0) { 230 | assert(owner); 231 | } 232 | 233 | EdgePtr(Collectable* owner, const RootPtr& other) : _owner(owner), _ptr(other._ptr) { 234 | assert(owner); 235 | _Retain(); 236 | } 237 | 238 | ~EdgePtr() { 239 | 240 | // If we're not in the GC thread. 241 | if(! Collector::GetInstance().InGC()) { 242 | _Release(); 243 | } 244 | 245 | } 246 | 247 | EdgePtr& operator=(const EdgePtr& other) { 248 | assert(_owner == other._owner); 249 | if(_ptr != other._ptr) { 250 | _Release(); 251 | _ptr = other._ptr; 252 | _Retain(); 253 | } 254 | return *this; 255 | } 256 | 257 | template 258 | EdgePtr& operator=(const RootPtr& other) { 259 | if(_ptr != other.Get()) { 260 | _Release(); 261 | _ptr = other.Get(); 262 | _Retain(); 263 | } 264 | return *this; 265 | } 266 | 267 | // Create a RootPtr out of this EdgePtr. 268 | RootPtr GetRootPtr() const { return RootPtr(_ptr); } 269 | 270 | operator bool() const { return _ptr != 0; } 271 | 272 | bool operator==(const EdgePtr& other) const { 273 | return _ptr == other._ptr; 274 | } 275 | 276 | bool operator!=(const EdgePtr& other) const { 277 | return _ptr != other._ptr; 278 | } 279 | 280 | bool operator<(const EdgePtr& other) const { 281 | return _ptr < other._ptr; 282 | } 283 | 284 | private: 285 | 286 | void _Retain() { 287 | if(_ptr) { 288 | Collector::GetInstance().AddEdge(_owner, _ptr); 289 | } 290 | } 291 | 292 | void _Release() { 293 | if(_ptr) { 294 | Collector::GetInstance().RemoveEdge(_owner, _ptr); 295 | } 296 | } 297 | 298 | Collectable* _owner; 299 | T* _ptr; 300 | 301 | }; // class EdgePtr 302 | 303 | #endif /* defined(__Dev__Collector__) */ 304 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Taylor Holliday 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | collector 2 | ========= 3 | 4 | A concurrent garbage collector for C++. Call it periodically from a background thread to sweep up your junk. 5 | 6 | ### Advangates 7 | 8 | * Unlike reference counting, handles cycles. 9 | * Usable in a real-time thread, because collection can occur in another thread. (Just need to collect often enough, see below) 10 | * Supports multiple mutator threads. ("Mutator" threads are just your threads that aren't the collector thread.) 11 | * Coexists peacefully with other forms of C++ memory management. 12 | * Offers the same conncurrency guarantees as `shared_ptr` (I think, hah) 13 | * Battle-tested in a real app (I haven't attributed any bugs to the collector, but I make no guarantees!) 14 | * < 500 lines and dirt simple. 15 | 16 | ### Disadvantages 17 | 18 | * The author is a noob. He's just a graphics programmer with no real experience with GC. 19 | * Intrusive. You need to derive from `Collectable`. Has to be intrusive so virtual destructors can be called from the collector. 20 | * Requires a little discipline with use of `RootPtr` and `EdgePtr` smart pointers. 21 | * Pointer assignment is probably super slow because it puts an event on a queue (I haven't profiled it). 22 | * Currently, if you don't collect often enough, a fixed-size queue will fill up and you'll block. 23 | * Collection isn't optimized. 24 | * Your code can block if you don't collect often enough. 25 | 26 | ### How to Use 27 | 28 | You'll need [Boost](http://www.boost.org) and C++11. Drop `Collector.cpp` and `Collector.hpp` in your project. 29 | 30 | Let's say you're doing a graph data structure. You might have something like this: 31 | 32 | ```c++ 33 | class Node : public Collectable { 34 | 35 | public: 36 | 37 | // Pass a pointer to a Node via a RootPtr, since it's on the stack. 38 | void AddEdge(const RootPtr &node) { 39 | 40 | // Store pointers in the heap as EdgePtrs. 41 | _edges.push_back( EdgePtr(this, node) ); 42 | } 43 | 44 | private: 45 | 46 | std::vector< EdgePtr< Node > > _edges; 47 | }; 48 | ``` 49 | 50 | **The basic idea is whenever you are pointing to a `Collectable` on the stack, use a `RootPtr`. For pointers living in the heap, use an `EdgePtr`**. (Maybe they should be called `StackPtr` and `HeapPtr`. I just use terms from the GC research papers I can't understand.) 51 | 52 | To create a collected object in the example, we'd do: 53 | 54 | ```c++ 55 | RootPtr node(new Node); // The collector uses the normal new and delete allocators. 56 | ``` 57 | 58 | There are two constructors for `EdgePtr`: 59 | 60 | * `EdgePtr::EdgePtr(Collectable* owner)` Creates a NULL `EdgePtr` with an owner. 61 | * `EdgePtr::EdgePtr(Collectable* owner, const RootPtr& p)` Creates an `EdgePtr` from `owner` to `p`. 62 | 63 | You can shoot yourself in the foot by forgetting about the `owner`. In practice I've found it pretty easy to avoid doing so. 64 | 65 | When you want to collect, just call: `Collector::GetInstance().Collect()` periodically from a background thread. If you only call it from the main thread, your code can deadlock. To reduce blocking, you can call `Collector::GetInstance().ProcessEvents()` in the background thread more often than you call `Collect`. My collector thread looks like this: 66 | 67 | ```c++ 68 | int n = 0; 69 | while (go) { 70 | 71 | // Update frequently to keep the event 72 | // queue from filling up. 73 | Collector::GetInstance().ProcessEvents(); 74 | 75 | // Collect occasionally. 76 | if ((++n % 100) == 0) { 77 | Collector::GetInstance().Collect(); 78 | } 79 | 80 | // Sleep for .01 seconds. 81 | boost::this_thread::sleep(boost::posix_time::millisec(10)); 82 | 83 | } 84 | ``` 85 | 86 | Enjoy! 87 | 88 | ### Todo 89 | * Reduce the possibility that the mutator thread will block 90 | * Add some debug sanity checks for `RootPtr` and `EdgePtr`. 91 | * Be less of a noob. --------------------------------------------------------------------------------