├── benchmark ├── allocate-rndtypes.cpp ├── allocate.cpp ├── benchmark.h ├── mark-many-rndtypes.cpp └── mark-many.cpp ├── picogc.h ├── picogc └── util.h └── t ├── exception_in_ctor.cpp ├── gc_in_ctor.cpp ├── has_members.cpp ├── inner-scope-assign.cpp ├── simple.cpp ├── stack.cpp └── test.h /benchmark/allocate-rndtypes.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cO2 3 | 4 | #include "benchmark/benchmark.h" 5 | 6 | #define LOOP_CNT 10000000 7 | 8 | template struct malloc_obj_t { 9 | std::string s_[SIZE]; // so that ~malloc_obj_t() will not be empty 10 | }; 11 | 12 | template struct gc_obj_t : public picogc::gc_object { 13 | std::string s_[SIZE]; // ditto 14 | }; 15 | 16 | int main(int argc, char** argv) 17 | { 18 | { // normal case 19 | benchmark_t bench("malloc"); 20 | rng_t rng; 21 | 22 | for (int i = 0; i < LOOP_CNT; ++i) { 23 | switch ((rng() >> 8) & 15) { 24 | #define CASE(n) case n: delete new malloc_obj_t; break 25 | CASE(0); 26 | CASE(1); 27 | CASE(2); 28 | CASE(3); 29 | CASE(4); 30 | CASE(5); 31 | CASE(6); 32 | CASE(7); 33 | CASE(8); 34 | CASE(9); 35 | CASE(10); 36 | CASE(11); 37 | CASE(12); 38 | CASE(13); 39 | CASE(14); 40 | CASE(15); 41 | #undef CASE 42 | } 43 | } 44 | } 45 | 46 | picogc::gc gc; 47 | picogc::gc_scope gc_scope(&gc); 48 | { // GC case 49 | benchmark_t bench("picogc"); 50 | picogc::scope scope; 51 | rng_t rng; 52 | 53 | for (int i = 0; i < LOOP_CNT / 100; ++i) { 54 | picogc::scope scope; 55 | for (int j = 0; j < 100; ++j) { 56 | switch ((rng() >> 8) & 15) { 57 | #define CASE(n) case n: new (picogc::IS_ATOMIC) gc_obj_t; break 58 | CASE(0); 59 | CASE(1); 60 | CASE(2); 61 | CASE(3); 62 | CASE(4); 63 | CASE(5); 64 | CASE(6); 65 | CASE(7); 66 | CASE(8); 67 | CASE(9); 68 | CASE(10); 69 | CASE(11); 70 | CASE(12); 71 | CASE(13); 72 | CASE(14); 73 | CASE(15); 74 | #undef CASE 75 | } 76 | } 77 | } 78 | 79 | gc.trigger_gc(); 80 | } 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /benchmark/allocate.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cO2 3 | 4 | #include "benchmark/benchmark.h" 5 | 6 | #define LOOP_CNT 10000000 7 | 8 | struct malloc_obj_t { 9 | int i_; 10 | malloc_obj_t() : i_(0) {} 11 | }; 12 | 13 | struct gc_obj_t : public picogc::gc_object { 14 | int i_; 15 | gc_obj_t() : i_(0) {} 16 | }; 17 | 18 | int main(int argc, char** argv) 19 | { 20 | { // normal case 21 | benchmark_t bench("malloc"); 22 | rng_t rng; 23 | 24 | for (int i = 0; i < LOOP_CNT; ++i) { 25 | delete new malloc_obj_t; 26 | } 27 | } 28 | 29 | picogc::gc gc; 30 | picogc::gc_scope gc_scope(&gc); 31 | { // GC case 32 | benchmark_t bench("picogc"); 33 | picogc::scope scope; 34 | rng_t rng; 35 | 36 | for (int i = 0; i < LOOP_CNT / 100; ++i) { 37 | picogc::scope scope; 38 | for (int j = 0; j < 100; ++j) { 39 | new (picogc::IS_ATOMIC) gc_obj_t; 40 | } 41 | } 42 | 43 | gc.trigger_gc(); 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /benchmark/benchmark.h: -------------------------------------------------------------------------------- 1 | #ifndef benchmark_h 2 | #define benchmark_h 3 | 4 | extern "C" { 5 | #include 6 | } 7 | #include "picogc.h" 8 | 9 | class benchmark_t { 10 | std::string name_; 11 | double start_; 12 | public: 13 | benchmark_t(const std::string& name) : name_(name), start_(now()) {} 14 | ~benchmark_t() { 15 | std::cout << name_ << "\t" << (now() - start_) << std::endl; 16 | } 17 | static double now() { 18 | rusage ru; 19 | getrusage(RUSAGE_SELF, &ru); 20 | return ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1000000.0; 21 | } 22 | }; 23 | 24 | class rng_t { 25 | unsigned n_; 26 | public: 27 | rng_t() : n_(1) {} 28 | int operator()() { 29 | n_ = n_ * 1103515245 + 12345; 30 | return n_ & 0x7fffffff; 31 | } 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /benchmark/mark-many-rndtypes.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cO2 -cDNDEBUG 3 | 4 | #include "benchmark/benchmark.h" 5 | #include "picogc/util.h" 6 | 7 | #define MARK_CNT 100000 8 | #define LOOP_CNT 10000000 9 | 10 | struct malloc_link_t { 11 | malloc_link_t* next; 12 | malloc_link_t() : next(NULL) {} 13 | }; 14 | 15 | template struct malloc_link_tmpl_t : public malloc_link_t { 16 | std::string s_[SIZE]; // so that dtor will not be empty 17 | }; 18 | 19 | struct gc_link_t : public picogc::gc_object { 20 | gc_link_t* next; 21 | gc_link_t() : next(NULL) { 22 | //printf("alloc %p\n", this); 23 | } 24 | ~gc_link_t() { 25 | //printf("free %p\n", this); 26 | next = (gc_link_t*)0xdeadbaad; 27 | } 28 | void gc_mark(picogc::gc* gc) { 29 | gc->mark(next); 30 | } 31 | }; 32 | 33 | template struct gc_link_tmpl_t : public gc_link_t { 34 | std::string s_[SIZE]; 35 | }; 36 | 37 | #define RND() ((rng() >> 8) & 15) 38 | 39 | inline malloc_link_t* create_malloc_link(rng_t& rng) 40 | { 41 | switch (RND()) { 42 | #define CASE(n) case n: return new malloc_link_tmpl_t 43 | CASE(0); 44 | CASE(1); 45 | CASE(2); 46 | CASE(3); 47 | CASE(4); 48 | CASE(5); 49 | CASE(6); 50 | CASE(7); 51 | CASE(8); 52 | CASE(9); 53 | CASE(10); 54 | CASE(11); 55 | CASE(12); 56 | CASE(13); 57 | CASE(14); 58 | CASE(15); 59 | #undef CASE 60 | } 61 | } 62 | 63 | inline gc_link_t* create_gc_link(rng_t& rng) 64 | { 65 | switch (RND()) { 66 | #define CASE(n) case n: return new gc_link_tmpl_t 67 | CASE(0); 68 | CASE(1); 69 | CASE(2); 70 | CASE(3); 71 | CASE(4); 72 | CASE(5); 73 | CASE(6); 74 | CASE(7); 75 | CASE(8); 76 | CASE(9); 77 | CASE(10); 78 | CASE(11); 79 | CASE(12); 80 | CASE(13); 81 | CASE(14); 82 | CASE(15); 83 | #undef CASE 84 | } 85 | } 86 | 87 | int main(int argc, char** argv) 88 | { 89 | { // normal case 90 | benchmark_t bench("malloc"); 91 | rng_t rng; 92 | 93 | malloc_link_t* head = create_malloc_link(rng); 94 | malloc_link_t* tail = head; 95 | for (int i = 0; i < MARK_CNT; ++i) { 96 | tail = tail->next = create_malloc_link(rng); 97 | } 98 | for (int i = 0; i < LOOP_CNT; ++i) { 99 | tail = tail->next = create_malloc_link(rng); 100 | malloc_link_t* t = head; 101 | head = head->next; 102 | delete t; 103 | } 104 | } 105 | 106 | picogc::gc gc; //(new picogc::config(102400)); 107 | //gc.emitter(new picogc::gc_log_emitter(stdout)); 108 | picogc::gc_scope gc_scope(&gc); 109 | { // GC case 110 | benchmark_t bench("picogc"); 111 | rng_t rng; 112 | picogc::scope scope; 113 | 114 | picogc::local head; 115 | { 116 | picogc::scope scope; 117 | head = create_gc_link(rng); 118 | } 119 | picogc::local tail = head; 120 | { 121 | picogc::scope scope; 122 | for (int i = 0; i < MARK_CNT; ++i) { 123 | tail->next = create_gc_link(rng); 124 | tail = tail->next; 125 | } 126 | } 127 | for (int i = 0; i < LOOP_CNT / 100; ++i) { 128 | picogc::scope scope; 129 | for (int j = 0; j < 100; ++j) { 130 | //printf("%p -> %p\n", &*head, head->next); fflush(stdout); 131 | tail->next = create_gc_link(rng); 132 | //printf("tail %p -> %p\n", &*tail, tail->next); fflush(stdout); 133 | tail = tail->next; 134 | head = head->next; 135 | } 136 | } 137 | gc.trigger_gc(); 138 | } 139 | 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /benchmark/mark-many.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cO2 -cDNDEBUG 3 | 4 | #include "benchmark/benchmark.h" 5 | 6 | #define MARK_CNT 100000 7 | #define LOOP_CNT 10000000 8 | 9 | struct malloc_link_t { 10 | malloc_link_t* next; 11 | malloc_link_t() : next(NULL) {} 12 | }; 13 | 14 | struct gc_link_t : public picogc::gc_object { 15 | gc_link_t* next; 16 | gc_link_t() : next(NULL) { 17 | //printf("alloc %p\n", this); 18 | } 19 | ~gc_link_t() { 20 | //printf("free %p\n", this); 21 | next = (gc_link_t*)0xdeadbaad; 22 | } 23 | void gc_mark(picogc::gc* gc) { 24 | gc->mark(next); 25 | } 26 | }; 27 | 28 | int main(int argc, char** argv) 29 | { 30 | { // normal case 31 | benchmark_t bench("malloc"); 32 | 33 | malloc_link_t* head = new malloc_link_t; 34 | malloc_link_t* tail = head; 35 | for (int i = 0; i < MARK_CNT; ++i) { 36 | tail = tail->next = new malloc_link_t; 37 | } 38 | for (int i = 0; i < LOOP_CNT; ++i) { 39 | tail = tail->next = new malloc_link_t; 40 | malloc_link_t* t = head; 41 | head = head->next; 42 | delete t; 43 | } 44 | } 45 | 46 | picogc::gc gc; //(new picogc::config(102400)); 47 | //gc.emitter(new picogc::gc_log_emitter(stdout)); 48 | picogc::gc_scope gc_scope(&gc); 49 | { // GC case 50 | benchmark_t bench("picogc"); 51 | picogc::scope scope; 52 | 53 | picogc::local head; 54 | { 55 | picogc::scope scope; 56 | head = new gc_link_t; 57 | } 58 | picogc::local tail = head; 59 | { 60 | picogc::scope scope; 61 | for (int i = 0; i < MARK_CNT; ++i) { 62 | tail->next = new gc_link_t; 63 | tail = tail->next; 64 | } 65 | } 66 | for (int i = 0; i < LOOP_CNT / 100; ++i) { 67 | picogc::scope scope; 68 | for (int j = 0; j < 100; ++j) { 69 | //printf("%p -> %p\n", &*head, head->next); fflush(stdout); 70 | tail->next = new gc_link_t; 71 | //printf("tail %p -> %p\n", &*tail, tail->next); fflush(stdout); 72 | tail = tail->next; 73 | head = head->next; 74 | } 75 | } 76 | gc.trigger_gc(); 77 | } 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /picogc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Kazuho Oku 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 14 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 16 | * EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 18 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 19 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 20 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | * 24 | * The views and conclusions contained in the software and documentation are 25 | * those of the authors and should not be interpreted as representing official 26 | * policies, either expressed or implied, of the author. 27 | * 28 | */ 29 | #ifndef picogc_h 30 | #define picogc_h 31 | 32 | extern "C" { 33 | #include 34 | } 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | namespace picogc { 41 | 42 | // internal flags 43 | enum { 44 | _FLAG_MARKED = 1, 45 | _FLAG_HAS_GC_MEMBERS = 2, 46 | _FLAG_MASK = 3 47 | }; 48 | 49 | // external flags 50 | enum { 51 | IS_ATOMIC = 0x1, 52 | IMMEDIATELY_TRACEABLE = 0x2, 53 | MAY_TRIGGER_GC = 0x4 54 | }; 55 | 56 | class gc; 57 | class gc_object; 58 | 59 | template class _stack { 60 | struct node { 61 | value_type values[VALUES_PER_NODE]; 62 | node *prev; // null if bottom 63 | }; 64 | node* node_; 65 | node* reserved_node_; 66 | value_type* top_; 67 | public: 68 | class iterator { 69 | node* node_; 70 | value_type* cur_; 71 | public: 72 | iterator(_stack& s) : node_(s.node_), cur_(s.top_) {} 73 | value_type* get() { 74 | if (cur_ == node_->values) { 75 | if (node_->prev == NULL) { 76 | return NULL; 77 | } 78 | node_ = node_->prev; 79 | cur_ = node_->values + VALUES_PER_NODE; 80 | } 81 | return --cur_; 82 | } 83 | }; 84 | _stack() : node_(new node), reserved_node_(NULL), top_(node_->values) { 85 | node_->prev = NULL; 86 | } 87 | ~_stack() { 88 | delete reserved_node_; 89 | while (node_ != NULL) { 90 | node* n = node_->prev; 91 | delete node_; 92 | node_ = n; 93 | } 94 | } 95 | bool empty() const { 96 | return node_->prev == NULL && top_ == node_->values; 97 | } 98 | value_type* push() { 99 | if (top_ == node_->values + VALUES_PER_NODE) { 100 | node* new_node; 101 | if (reserved_node_ != NULL) { 102 | new_node = reserved_node_; 103 | reserved_node_ = NULL; 104 | } else { 105 | new_node = new node; 106 | } 107 | new_node->prev = node_; 108 | node_ = new_node; 109 | top_ = new_node->values; 110 | } 111 | return top_++; 112 | } 113 | value_type* pop() { 114 | if (top_ == node_->values) { 115 | if (node_->prev == NULL) { 116 | return NULL; 117 | } 118 | delete reserved_node_; 119 | reserved_node_ = node_; 120 | node_ = node_->prev; 121 | top_ = node_->values + VALUES_PER_NODE; 122 | } 123 | return --top_; 124 | } 125 | value_type* preserve() { 126 | return top_; 127 | } 128 | void restore(value_type* slot) { 129 | node* n = node_; 130 | while (! (n->values <= slot && slot <= n->values + VALUES_PER_NODE)) { 131 | if (reserved_node_ == NULL) { 132 | reserved_node_ = n; 133 | n = n->prev; 134 | } else { 135 | node* prev = n->prev; 136 | delete n; 137 | n = prev; 138 | } 139 | } 140 | // found 141 | node_ = n; 142 | top_ = slot; 143 | } 144 | }; 145 | 146 | struct config { 147 | size_t gc_interval_bytes_; 148 | config() : gc_interval_bytes_(8 * 1024 * 1024) {} 149 | size_t gc_interval_bytes() const { return gc_interval_bytes_; } 150 | config& gc_interval_bytes(size_t v) { 151 | gc_interval_bytes_ = v; 152 | return *this; 153 | } 154 | }; 155 | 156 | struct gc_stats { 157 | size_t on_stack; 158 | size_t slowly_marked; 159 | size_t not_collected; 160 | size_t collected; 161 | gc_stats() : on_stack(0), slowly_marked(0), not_collected(0), collected(0) 162 | {} 163 | }; 164 | 165 | struct gc_emitter { 166 | virtual ~gc_emitter() {} 167 | virtual void gc_start(gc*) {} 168 | virtual void gc_end(gc*, const gc_stats&) {} 169 | virtual void mark_start(gc*) {} 170 | virtual void mark_end(gc*) {} 171 | virtual void sweep_start(gc*) {} 172 | virtual void sweep_end(gc*) {} 173 | }; 174 | 175 | // global variables 176 | template struct _globals { 177 | static config default_config; 178 | static gc_emitter default_emitter; 179 | static gc* _top_scope; 180 | }; 181 | template config _globals::default_config; 182 | template gc_emitter _globals::default_emitter; 183 | template gc* _globals::_top_scope; 184 | typedef _globals globals; 185 | 186 | template class local { 187 | gc_object** slot_; 188 | public: 189 | local(T* obj = NULL); 190 | local(const local& x); 191 | local& operator=(const local& x) { *slot_ = *x.slot_; return *this; } 192 | local& operator=(T* obj) { *slot_ = obj; return *this; } 193 | T* get() const { return static_cast(*slot_); } 194 | operator T*() const { return get(); } 195 | T* operator->() const { return get(); } 196 | }; 197 | 198 | class gc_scope { 199 | gc* prev_; 200 | public: 201 | gc_scope(gc* gc) : prev_(globals::_top_scope) { 202 | globals::_top_scope = gc; 203 | } 204 | ~gc_scope() { 205 | globals::_top_scope = prev_; 206 | } 207 | }; 208 | 209 | class scope { 210 | friend class gc; 211 | gc_object* new_head_; 212 | intptr_t* new_tail_slot_; 213 | scope* prev_; 214 | gc_object** stack_state_; 215 | void _destruct(gc* gc); 216 | public: 217 | scope(); 218 | ~scope(); 219 | template T* close(T* obj); 220 | }; 221 | 222 | class gc { 223 | friend class scope; 224 | scope* scope_; 225 | _stack stack_; 226 | gc_object* obj_head_; 227 | _stack pending_; 228 | size_t bytes_allocated_since_gc_; 229 | config conf_; 230 | gc_emitter* emitter_; 231 | public: 232 | gc(const config& conf = config()) 233 | : scope_(NULL), stack_(), obj_head_(NULL), pending_(), 234 | bytes_allocated_since_gc_(0), conf_(conf), 235 | emitter_(&globals::default_emitter) 236 | {} 237 | ~gc(); 238 | void* allocate(size_t sz, int flags); 239 | void trigger_gc(); 240 | void may_trigger_gc(); 241 | void mark(gc_object* obj); 242 | gc_object** _acquire_local_slot() { 243 | return stack_.push(); 244 | } 245 | gc_emitter* emitter() { return emitter_; } 246 | void emitter(gc_emitter* emitter) { emitter_ = emitter; } 247 | static gc* top() { 248 | assert(globals::_top_scope != NULL); 249 | return globals::_top_scope; 250 | } 251 | protected: 252 | virtual void _mark(gc_stats& stats); 253 | virtual void _sweep(gc_stats& stats); 254 | }; 255 | 256 | class gc_object { 257 | friend class gc; 258 | intptr_t next_; 259 | gc_object(const gc_object&); // = delete; 260 | gc_object& operator=(const gc_object&); // = delete; 261 | protected: 262 | gc_object() /* next_ is initialized in operator new */ {} 263 | virtual ~gc_object() {} 264 | virtual void gc_mark(picogc::gc* gc) {} 265 | public: 266 | bool gc_is_marked() const { return (next_ & _FLAG_MARKED) != 0; } 267 | static void* operator new(size_t sz); 268 | static void* operator new(size_t sz, int flags); 269 | static void operator delete(void* p); 270 | static void operator delete(void* p, int flags); 271 | private: 272 | static void* operator new(size_t, void* buf) { return buf; } 273 | }; 274 | 275 | template 276 | inline local::local(T* obj) : slot_(gc::top()->_acquire_local_slot()) 277 | { 278 | *slot_ = obj; 279 | } 280 | 281 | template 282 | inline local::local(const local& x) 283 | : slot_(gc::top()->_acquire_local_slot()) 284 | { 285 | *slot_ = *x.slot_; 286 | } 287 | 288 | inline scope::scope() : new_head_(NULL), new_tail_slot_(NULL) 289 | { 290 | gc* gc = gc::top(); 291 | prev_ = gc->scope_; 292 | gc->scope_ = this; 293 | stack_state_ = gc->stack_.preserve(); 294 | } 295 | 296 | inline void scope::_destruct(gc* gc) 297 | { 298 | gc->stack_.restore(stack_state_); 299 | gc->scope_ = prev_; 300 | if (new_head_ != NULL) { 301 | *new_tail_slot_ |= reinterpret_cast(gc->obj_head_); 302 | gc->obj_head_ = new_head_; 303 | } 304 | } 305 | 306 | inline scope::~scope() 307 | { 308 | gc* gc = gc::top(); 309 | if (stack_state_ != NULL) 310 | _destruct(gc); 311 | gc->may_trigger_gc(); 312 | } 313 | 314 | template inline T* scope::close(T* obj) { 315 | gc* gc = gc::top(); 316 | // destruct the frame, and push the returning value on the prev frame 317 | _destruct(gc); 318 | stack_state_ = NULL; 319 | *gc->stack_.push() = static_cast(obj); 320 | return obj; 321 | } 322 | 323 | inline gc::~gc() 324 | { 325 | // free all objs 326 | for (gc_object* o = obj_head_; o != NULL; ) { 327 | gc_object* next = reinterpret_cast(o->next_ & ~_FLAG_MASK); 328 | o->~gc_object(); 329 | ::operator delete(static_cast(o)); 330 | o = next; 331 | } 332 | } 333 | 334 | inline void* gc::allocate(size_t sz, int flags) 335 | { 336 | bytes_allocated_since_gc_ += sz; 337 | if ((flags & MAY_TRIGGER_GC) != 0) { 338 | may_trigger_gc(); 339 | } 340 | gc_object* p = static_cast(::operator new(sz)); 341 | // GC might walk through the object during construction 342 | if ((flags & IS_ATOMIC) == 0) { 343 | memset(static_cast(p), 0, sz); 344 | } 345 | // register to GC list 346 | if ((flags & IMMEDIATELY_TRACEABLE) != 0) { 347 | p->next_ = reinterpret_cast(obj_head_) 348 | | ((flags & IS_ATOMIC) != 0 ? 0 : _FLAG_HAS_GC_MEMBERS); 349 | obj_head_ = p; 350 | } else { 351 | scope* scope = scope_; 352 | if (scope->new_head_ == NULL) 353 | scope->new_tail_slot_ = &p->next_; 354 | p->next_ = reinterpret_cast(scope->new_head_) 355 | | ((flags & IS_ATOMIC) != 0 ? 0 : _FLAG_HAS_GC_MEMBERS); 356 | scope->new_head_ = p; 357 | } 358 | return p; 359 | } 360 | 361 | inline void gc::_mark(gc_stats& stats) 362 | { 363 | // mark all the objects 364 | gc_object** slot; 365 | while ((slot = pending_.pop()) != NULL) { 366 | // request deferred marking of the properties 367 | stats.slowly_marked++; 368 | (*slot)->gc_mark(this); 369 | } 370 | } 371 | 372 | inline void gc::_sweep(gc_stats& stats) 373 | { 374 | // collect unmarked objects, as well as clearing the mark of live objects 375 | intptr_t* ref = reinterpret_cast(&obj_head_); 376 | for (gc_object* obj = obj_head_; obj != NULL; ) { 377 | intptr_t next = obj->next_; 378 | if ((next & _FLAG_MARKED) != 0) { 379 | // alive, clear the mark and connect to the list 380 | *ref = reinterpret_cast(obj) | (*ref & _FLAG_HAS_GC_MEMBERS); 381 | ref = &obj->next_; 382 | stats.not_collected++; 383 | } else { 384 | // dead, destroy 385 | obj->~gc_object(); 386 | ::operator delete(static_cast(obj)); 387 | stats.collected++; 388 | } 389 | obj = reinterpret_cast(next & ~_FLAG_MASK); 390 | } 391 | *ref &= _FLAG_HAS_GC_MEMBERS; 392 | } 393 | 394 | inline void gc::trigger_gc() 395 | { 396 | assert(pending_.empty()); 397 | 398 | emitter_->gc_start(this); 399 | gc_stats stats; 400 | 401 | // setup new 402 | for (scope* scope = scope_; scope != NULL; scope = scope->prev_) { 403 | for (gc_object* o = scope_->new_head_; 404 | o != NULL; 405 | o = reinterpret_cast(o->next_ & ~_FLAG_MASK)) { 406 | mark(o); 407 | stats.on_stack++; 408 | } 409 | } 410 | { // setup local 411 | _stack::iterator iter(stack_); 412 | gc_object** o; 413 | while ((o = iter.get()) != NULL) { 414 | mark(*o); 415 | stats.on_stack++; 416 | } 417 | } 418 | 419 | // mark 420 | emitter_->mark_start(this); 421 | _mark(stats); 422 | emitter_->mark_end(this); 423 | // sweep 424 | emitter_->sweep_start(this); 425 | _sweep(stats); 426 | emitter_->sweep_end(this); 427 | 428 | // clear the marks in new (as well as count the number) 429 | for (scope* scope = scope_; scope != NULL; scope = scope->prev_) { 430 | for (gc_object* o = scope_->new_head_; 431 | o != NULL; 432 | o = reinterpret_cast(o->next_ & ~_FLAG_MASK)) { 433 | o->next_ &= ~_FLAG_MARKED; 434 | stats.not_collected++; 435 | } 436 | } 437 | 438 | emitter_->gc_end(this, stats); 439 | } 440 | 441 | inline void gc::may_trigger_gc() 442 | { 443 | if (bytes_allocated_since_gc_ >= conf_.gc_interval_bytes()) { 444 | trigger_gc(); 445 | bytes_allocated_since_gc_ = 0; 446 | } 447 | } 448 | 449 | inline void gc::mark(gc_object* obj) 450 | { 451 | if (obj == NULL) 452 | return; 453 | // return if already marked 454 | if ((obj->next_ & _FLAG_MARKED) != 0) 455 | return; 456 | // mark 457 | obj->next_ |= _FLAG_MARKED; 458 | // push to the mark stack 459 | if ((obj->next_ & _FLAG_HAS_GC_MEMBERS) != 0) 460 | *pending_.push() = obj; 461 | } 462 | 463 | inline void* gc_object::operator new(size_t sz) 464 | { 465 | return gc::top()->allocate(sz, 0); 466 | } 467 | 468 | inline void* gc_object::operator new(size_t sz, int flags) 469 | { 470 | return gc::top()->allocate(sz, flags); 471 | } 472 | 473 | // only called when an exception is raised within ctor 474 | inline void gc_object::operator delete(void* p) 475 | { 476 | // vtbl should point to an empty dtor 477 | new (p) gc_object; 478 | } 479 | 480 | inline void gc_object::operator delete(void* p, int) 481 | { 482 | gc_object::operator delete(p); 483 | } 484 | 485 | } 486 | 487 | #endif 488 | -------------------------------------------------------------------------------- /picogc/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Kazuho Oku 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the documentation 11 | * and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 14 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 16 | * EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 18 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 19 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 20 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | * 24 | * The views and conclusions contained in the software and documentation are 25 | * those of the authors and should not be interpreted as representing official 26 | * policies, either expressed or implied, of the author. 27 | * 28 | */ 29 | #ifndef picogc_util_h 30 | #define picogc_util_h 31 | 32 | #include 33 | extern "C" { 34 | #include 35 | } 36 | 37 | // please include picogc.h by yourself 38 | 39 | namespace picogc { 40 | 41 | class gc_log_emitter : public gc_emitter { 42 | FILE* fp_; 43 | double mark_time_; 44 | double sweep_time_; 45 | struct { 46 | double mark_time; 47 | double sweep_time; 48 | gc_stats stats; 49 | } accumulated_; 50 | static double now() { 51 | rusage ru; 52 | getrusage(RUSAGE_SELF, &ru); 53 | return ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1000000.0; 54 | } 55 | public: 56 | gc_log_emitter(FILE* fp) : fp_(fp) { 57 | accumulated_.mark_time = 0; 58 | accumulated_.sweep_time = 0; 59 | memset(&accumulated_.stats, 0, sizeof(accumulated_.stats)); 60 | } 61 | virtual void gc_start(gc*) { 62 | fprintf(fp_, "--- picogc - garbage collection ---\n"); 63 | fflush(fp_); 64 | } 65 | virtual void gc_end(gc*, const gc_stats& stats) { 66 | accumulated_.mark_time += mark_time_; 67 | accumulated_.sweep_time += sweep_time_; 68 | accumulated_.stats.on_stack += stats.on_stack; 69 | accumulated_.stats.slowly_marked += stats.slowly_marked; 70 | accumulated_.stats.not_collected += stats.not_collected; 71 | accumulated_.stats.collected += stats.collected; 72 | fprintf(fp_, 73 | "mark_time: %f (%f)\n" 74 | "sweep_time: %f (%f)\n" 75 | "on_stack: %zd (%zd)\n" 76 | "slowly_marked: %zd (%zd)\n" 77 | "not_collected: %zd (%zd)\n" 78 | "collected: %zd (%zd)\n" 79 | "-----------------------------------\n", 80 | mark_time_, accumulated_.mark_time, 81 | sweep_time_, accumulated_.sweep_time, 82 | stats.on_stack, accumulated_.stats.on_stack, 83 | stats.slowly_marked, accumulated_.stats.slowly_marked, 84 | stats.not_collected, accumulated_.stats.not_collected, 85 | stats.collected, accumulated_.stats.collected); 86 | fflush(fp_); 87 | } 88 | virtual void mark_start(gc*) { 89 | mark_time_ = now(); 90 | } 91 | virtual void mark_end(gc*) { 92 | mark_time_ = now() - mark_time_; 93 | } 94 | virtual void sweep_start(gc*) { 95 | sweep_time_ = now(); 96 | } 97 | virtual void sweep_end(gc*) { 98 | sweep_time_ = now() - sweep_time_; 99 | } 100 | }; 101 | 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /t/exception_in_ctor.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cg 3 | 4 | #include 5 | #include "picogc.h" 6 | #include "t/test.h" 7 | 8 | using namespace std; 9 | 10 | struct picogc::gc_stats gc_stats; 11 | 12 | struct gc_emitter : public picogc::gc_emitter { 13 | virtual void gc_end(picogc::gc*, const picogc::gc_stats& stats) { 14 | gc_stats = stats; 15 | } 16 | }; 17 | 18 | struct K : public picogc::gc_object { 19 | static size_t dtor_called_; 20 | K() { 21 | throw 123; 22 | } 23 | ~K() { 24 | ++dtor_called_; 25 | } 26 | }; 27 | 28 | size_t K::dtor_called_ = 0; 29 | 30 | void test() 31 | { 32 | plan(5); 33 | 34 | picogc::gc gc; 35 | gc.emitter(new gc_emitter); 36 | picogc::gc_scope gc_scope(&gc); 37 | 38 | { 39 | picogc::scope scope; 40 | 41 | try { 42 | new K(); 43 | } catch (...) { 44 | } 45 | 46 | try { 47 | new (picogc::IS_ATOMIC) K(); 48 | } catch (...) { 49 | } 50 | 51 | gc.trigger_gc(); 52 | is(gc_stats.not_collected, (size_t)2); 53 | is(gc_stats.collected, (size_t)0); 54 | } 55 | 56 | gc.trigger_gc(); 57 | is(gc_stats.not_collected, (size_t)0); 58 | is(gc_stats.collected, (size_t)2); 59 | is(K::dtor_called_, (size_t)0); 60 | } 61 | -------------------------------------------------------------------------------- /t/gc_in_ctor.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cg 3 | 4 | #include "picogc.h" 5 | #include "t/test.h" 6 | 7 | static picogc::gc* gc; 8 | static picogc::gc_stats last_stats; 9 | static size_t num_created = 0; 10 | 11 | struct Linked : public picogc::gc_object { 12 | typedef picogc::gc_object super; 13 | Linked* linked_; 14 | Linked() { 15 | gc->trigger_gc(); 16 | is(last_stats.collected, (size_t) 0UL); 17 | if (num_created++ == 0) 18 | linked_ = new Linked(); 19 | } 20 | virtual void gc_mark(picogc::gc* gc) { 21 | super::gc_mark(gc); 22 | gc->mark(linked_); 23 | } 24 | }; 25 | 26 | struct Emitter : public picogc::gc_emitter { 27 | virtual void gc_end(picogc::gc*, const picogc::gc_stats& stats) { 28 | last_stats = stats; 29 | } 30 | }; 31 | 32 | void test() 33 | { 34 | plan(3); 35 | 36 | gc = new picogc::gc(); 37 | gc->emitter(new Emitter()); 38 | picogc::gc_scope gc_scope(gc); 39 | 40 | { 41 | picogc::scope scope; 42 | new Linked(); 43 | } 44 | 45 | gc->trigger_gc(); 46 | is(last_stats.collected, (size_t) 2UL); 47 | } 48 | -------------------------------------------------------------------------------- /t/has_members.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cg 3 | 4 | #include 5 | #include "picogc.h" 6 | #include "t/test.h" 7 | 8 | template struct K : public picogc::gc_object { 9 | static size_t count_; 10 | virtual void gc_mark(picogc::gc* gc) { 11 | ++count_; 12 | } 13 | }; 14 | 15 | template size_t K::count_; 16 | 17 | void test() 18 | { 19 | plan(2); 20 | 21 | picogc::gc gc; 22 | picogc::gc_scope gc_scope(&gc); 23 | 24 | { 25 | picogc::scope scope; 26 | 27 | new K; 28 | new (picogc::IS_ATOMIC) K; 29 | 30 | gc.trigger_gc(); 31 | is(K::count_, (size_t)1); 32 | is(K::count_, (size_t)0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /t/inner-scope-assign.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cg 3 | 4 | #include 5 | #include "picogc.h" 6 | #include "t/test.h" 7 | 8 | using namespace std; 9 | 10 | struct picogc::gc_stats gc_stats; 11 | 12 | struct K : public picogc::gc_object { 13 | bool* dtor_called_; 14 | K(bool* dtor_called) : dtor_called_(dtor_called) {} 15 | ~K() { 16 | *dtor_called_ = true; 17 | } 18 | }; 19 | 20 | void test() 21 | { 22 | plan(2); 23 | 24 | picogc::gc gc; 25 | picogc::gc_scope gc_scope(&gc); 26 | bool inner_dtor_called = false; 27 | 28 | { 29 | picogc::scope scope; 30 | picogc::local v = NULL; 31 | { 32 | v = new K(&inner_dtor_called); 33 | } 34 | gc.trigger_gc(); 35 | ok(! inner_dtor_called); 36 | } 37 | gc.trigger_gc(); 38 | ok(inner_dtor_called); 39 | } 40 | -------------------------------------------------------------------------------- /t/simple.cpp: -------------------------------------------------------------------------------- 1 | #! /usr/bin/C 2 | #option -cWall -p -cg 3 | 4 | #include 5 | #include 6 | #include "picogc.h" 7 | #include "t/test.h" 8 | 9 | using namespace std; 10 | 11 | static vector destroyed; 12 | 13 | struct Label : public picogc::gc_object { 14 | typedef picogc::gc_object super; 15 | string label_; 16 | Label* linked_; 17 | Label(const string& label) : label_(label) {} 18 | virtual ~Label() { 19 | destroyed.push_back(label_); 20 | } 21 | virtual void gc_mark(picogc::gc* gc) { 22 | super::gc_mark(gc); 23 | gc->mark(linked_); 24 | } 25 | }; 26 | 27 | Label* doit() 28 | { 29 | picogc::scope scope; 30 | 31 | picogc::local