MeixnerGC  1.0
gc_ptr.h
Go to the documentation of this file.
1 /*
2 Copyright (c) 2017 Dr. Matthias Meixner
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22 
23 #ifndef GC_PTR_H
24 #define GC_PTR_H
25 
28 #include <mutex>
29 #include <vector>
30 #include <cstddef>
31 #include <atomic>
32 
33 #ifndef DOXYGEN
34 namespace mxgc {
35 #endif
36 
37 class gc_base_ptr;
38 class gc_object;
39 
44  void gc_collect();
45 
46 extern std::mutex gc_mutex;
47 extern thread_local gc_object *current;
48 extern std::vector<gc_object *> all_objects;
49 extern long gc_counter;
52 class gc_object {
53 
54  template<class T> friend class make_gc;
55  friend class gc_base_ptr;
56  friend void gc_collect();
57 
58  private:
59  gc_object(const gc_object &)=delete;
60  void operator=(const gc_object &)=delete;
61 
62  protected:
63  void *end_;
70  void *start() noexcept { return (void *)(this+1); }
71 
75  void *end() noexcept { return end_; }
76 
80  void (*destructor)(void *s, void *e);
81 
83  std::atomic<int> root_ref_cnt;
84 
86  std::atomic<gc_base_ptr *> first;
87 
89  bool mark;
90 
91  public:
92 
94  gc_object() noexcept;
95 
100  gc_object(void *e,void (*d)(void *,void *)) noexcept;
101 
103  ~gc_object();
104 };
105 
107 class gc_base_ptr {
108 
109  friend void gc_collect();
110 
111  static void gc_collect(gc_object *o);
112 
113  protected:
115  enum PtrType {
118  } type;
120  std::atomic<gc_base_ptr *> next;
121  std::atomic<gc_object *> object;
123  public:
125  explicit gc_base_ptr(gc_object *c=nullptr);
126 
128  gc_base_ptr(const gc_base_ptr &o) : gc_base_ptr(o.object) {};
129 
131  gc_base_ptr(gc_base_ptr &&o);
132 
134  void operator=(nullptr_t);
135 
137  void operator=(const gc_base_ptr &o);
138 
140  void operator=(gc_base_ptr &&o);
141 
143  void reset();
144 
146  ~gc_base_ptr();
147 };
148 
149 
151 template<class T> class gc_ptr: public gc_base_ptr {
152  template<class U> friend class gc_ptr;
153  protected:
154  T *ptr;
155  public:
156 
157 
159  gc_ptr():gc_base_ptr() { ptr=nullptr; }
160 
162  gc_ptr(nullptr_t):gc_base_ptr() { ptr=nullptr; }
163 
167  gc_ptr(const gc_ptr &o):gc_base_ptr(o) { ptr=o.ptr; }
168 
172  gc_ptr(gc_ptr &&o):gc_base_ptr(std::move(o)) { ptr=o.ptr; }
173 
179  gc_ptr(const gc_base_ptr &o, T *p):gc_base_ptr(o) { ptr=p; }
180 
181 
185  template<class U> gc_ptr(const gc_ptr<U> &o):gc_base_ptr(o) {
186  ptr=o.ptr;
187  }
188 
189 
193  gc_ptr &operator=(const gc_ptr &o) {
195  ptr=o.ptr;
196  return *this;
197  }
198 
203  gc_base_ptr::operator=(std::move(o));
204  ptr=o.ptr;
205  return *this;
206  }
207 
209  gc_ptr &operator=(nullptr_t) {
210  gc_base_ptr::operator=(nullptr);
211  ptr=nullptr;
212  return *this;
213  }
214 
215 
216 
218  T *get() const noexcept {return ptr;}
219 
221  operator T*() const noexcept { return ptr; }
222 
224  T &operator*() const noexcept {return *ptr;}
225 
227  T *operator->() const noexcept {return ptr;}
228 
230  T &operator[](int idx) const noexcept { return ptr[idx]; }
231 
233  void reset() { ptr=nullptr; gc_base_ptr::reset(); }
234 
236  gc_ptr<T> &operator++() noexcept { ++ptr; return *this; }
237 
239  gc_ptr<T> &operator--() noexcept { --ptr; return *this; }
240 
242  gc_ptr<T> operator++(int) noexcept { gc_ptr<T> r(*this); ptr++; return r; }
243 
245  gc_ptr<T> operator--(int) noexcept { gc_ptr<T> r(*this); ptr--; return r; }
246 
247 
249  template<class U> gc_ptr<T> &operator+=(const U &z) noexcept { ptr+=z; return *this; }
250 
252  template<class U> gc_ptr<T> &operator-=(const U &z) noexcept { ptr-=z; return *this; }
253 };
254 
255 
257 template<class T> class make_gc: public gc_ptr<T>
258 {
259  public:
264  template <class... Args> make_gc(Args&&... args)
265  {
266  // allocate memory for control object and T object
267  char *mem=(char*)::operator new(sizeof(gc_object)+sizeof(T));
268 
269  // creating the control object and pushing it on the stack requires a lock
270  gc_mutex.lock();
271 
272  if(gc_counter--==0) {
273  gc_mutex.unlock();
274  gc_collect();
275  gc_mutex.lock();
276  }
277 
278  // construct control object in allocated memory
279  gc_object *new_object=new(mem) gc_object(mem+sizeof(gc_object)+sizeof(T),
280  [](void *s, void *e) { T *t=(T*)s; t->~T(); });
281  gc_base_ptr::object.store(new_object,std::memory_order_relaxed);
282 
283 
284  // increment reference count if needed
285  if(gc_base_ptr::type==gc_base_ptr::ROOT) new_object->root_ref_cnt.store(1,std::memory_order_relaxed);
286 
287  // push object on object list
288  all_objects.push_back(new_object);
289 
290  gc_mutex.unlock();
291 
292  // store old current object in case we are in a recursion
293  // and set up new current object
294  gc_object *parent=current;
295  current=new_object;
296 
297  // set up pointer
298  gc_ptr<T>::ptr=(T*)new_object->start();
299 
300  // run the constructor of the allocated object
301  try {
302  new(new_object->start()) T(std::forward<Args>(args)...);
303  }
304  catch(...) {
305  // if it fails, release reference, so that GC will clean it up
307  gc_base_ptr::object=nullptr;
308  gc_ptr<T>::ptr=nullptr;
309  current=parent;
310  throw; // re-throw exception
311  }
312 
313  current=parent;
314  }
315 };
316 
318 template<class T> class make_gc<T[]>: public gc_ptr<T>
319 {
320  public:
325  make_gc(unsigned size)
326  {
327  // allocate memory for control object and T object
328  char *mem=(char*)::operator new(sizeof(gc_object)+size*sizeof(T));
329 
330  // creating the control object and pushing it on the stack requires a lock
331  gc_mutex.lock();
332 
333  if(gc_counter--==0) {
334  gc_mutex.unlock();
335  gc_collect();
336  gc_mutex.lock();
337  }
338 
339  gc_object *new_object=new(mem) gc_object(mem+sizeof(gc_object)+size*sizeof(T),
340  [](void *s, void *e) { for(T *t=(T*)e;--t>=s;) t->~T(); });
341  gc_base_ptr::object.store(new_object,std::memory_order_relaxed);
342 
343 
344  // increment reference count if needed
345  if(gc_base_ptr::type==gc_base_ptr::ROOT) new_object->root_ref_cnt.store(1,std::memory_order_relaxed);
346 
347  // push object on object list
348  all_objects.push_back(new_object);
349 
350  gc_mutex.unlock();
351 
352  // store old current object in case we are in a recursion
353  // and set up new current object
354  gc_object *parent=current;
355  current=new_object;
356 
357  T *start=(T*)new_object->start();
358  T *end=(T*)new_object->end();
359 
360  gc_ptr<T>::ptr=start;
361  T *i;
362  // run the constructor of the allocated object
363  try {
364  for(i=start;i!=end;i++) new(i) T();
365  }
366  catch(...) {
367  for(T *j=i;--j>=start;) j->~T();
369  gc_base_ptr::object=nullptr;
370  gc_ptr<T>::ptr=nullptr;
371  current=parent;
372  throw; // re-throw exception
373  }
374 
375  current=parent;
376  }
377 };
378 
384 
385 // comparison operators
386 
392 template <class T, class U> bool operator==(const gc_ptr<T>& lhs, const gc_ptr<U>& rhs) noexcept
393 {
394  return lhs.get()==rhs.get();
395 }
396 
402 template <class T> bool operator==(const gc_ptr<T>& lhs, nullptr_t n) noexcept
403 {
404  return lhs.get()==0;
405 }
406 
412 template <class T> bool operator==(nullptr_t n, const gc_ptr<T>& rhs) noexcept
413 {
414  return 0==rhs.get();
415 }
416 
422 template <class T, class U> bool operator!=(const gc_ptr<T>& lhs, const gc_ptr<U>& rhs) noexcept
423 {
424  return lhs.get()!=rhs.get();
425 }
426 
432 template <class T> bool operator!=(const gc_ptr<T>& lhs, nullptr_t n) noexcept
433 {
434  return lhs.get()!=0;
435 }
436 
442 template <class T> bool operator!=(nullptr_t n, const gc_ptr<T>& rhs) noexcept
443 {
444  return 0!=rhs.get();
445 }
446 
452 template <class T, class U> bool operator<(const gc_ptr<T>& lhs, const gc_ptr<U>& rhs) noexcept
453 {
454  return lhs.get()<rhs.get();
455 }
456 
462 template <class T> bool operator<(const gc_ptr<T>& lhs, nullptr_t n) noexcept
463 {
464  return lhs.get()<0;
465 }
466 
472 template <class T> bool operator<(nullptr_t n, const gc_ptr<T>& rhs) noexcept
473 {
474  return 0<rhs.get();
475 }
476 
482 template <class T> bool operator<=(const gc_ptr<T>& lhs, const gc_ptr<T>& rhs) noexcept
483 {
484  return lhs.get()<=rhs.get();
485 }
486 
492 template <class T> bool operator<=(const gc_ptr<T>& lhs, nullptr_t n) noexcept
493 {
494  return lhs.get()<=0;
495 }
496 
502 template <class T> bool operator<=(nullptr_t n, const gc_ptr<T>& rhs) noexcept
503 {
504  return 0<=rhs.get();
505 }
506 
512 template <class T> bool operator>(const gc_ptr<T>& lhs, const gc_ptr<T>& rhs) noexcept
513 {
514  return lhs.get()>rhs.get();
515 }
516 
522 template <class T> bool operator>(const gc_ptr<T>& lhs, nullptr_t n) noexcept
523 {
524  return lhs.get()>0;
525 }
526 
532 template <class T> bool operator>(nullptr_t n, const gc_ptr<T>& rhs) noexcept
533 {
534  return 0>rhs.get();
535 }
536 
542 template <class T> bool operator>=(const gc_ptr<T>& lhs, const gc_ptr<T>& rhs) noexcept
543 {
544  return lhs.get()>=rhs.get();
545 }
546 
552 template <class T> bool operator>=(const gc_ptr<T>& lhs, nullptr_t n) noexcept
553 {
554  return lhs.get()>=0;
555 }
556 
562 template <class T> bool operator>=(nullptr_t n, const gc_ptr<T>& rhs) noexcept
563 {
564  return 0>=rhs.get();
565 }
566 
567 
573 template<class T, class U> gc_ptr<T> operator+(const gc_ptr<T> &a, const U &b) noexcept
574 {
575  return gc_ptr<T>(a,a.get()+b);
576 }
577 
583 template<class T, class U> gc_ptr<T> operator+(const U &a, const gc_ptr<T> &b) noexcept
584 {
585  return gc_ptr<T>(b,b.get()+a);
586 }
587 
593 template<class T, class U> gc_ptr<T> operator-(const gc_ptr<T> &a, const U &b) noexcept
594 {
595  return gc_ptr<T>(a,a.get()-b);
596 }
597 
603 template<class T> auto operator-(const gc_ptr<T> &a, const gc_ptr<T> &b) noexcept -> decltype(a.get()-b.get())
604 {
605  return a.get()-b.get();
606 }
607 
613 
617 template <class T, class U> gc_ptr<T> static_pointer_cast(const gc_ptr<U>& p) noexcept
618 {
619  return gc_ptr<T>(p,static_cast<T*>(p.get()));
620 }
621 
625 template <class T, class U> gc_ptr<T> dynamic_pointer_cast(const gc_ptr<U>& p) noexcept
626 {
627  return gc_ptr<T>(p,dynamic_cast<T*>(p.get()));
628 }
629 
633 template <class T, class U> gc_ptr<T> const_pointer_cast(const gc_ptr<U>& p) noexcept
634 {
635  return gc_ptr<T>(p,const_cast<T*>(p.get()));
636 }
637 
641 template <class T, class U> gc_ptr<T> reinterpret_pointer_cast(const gc_ptr<U>& p) noexcept
642 {
643  return gc_ptr<T>(p,reinterpret_cast<T*>(p.get()));
644 }
645 
649 template <class T, class U> gc_ptr<T> pointer_cast(const gc_ptr<U>& p) noexcept
650 {
651  return gc_ptr<T>(p,(T*)(p.get()));
652 }
653 
654 #ifndef DOXYGEN
655 } // namespace
656 #endif
657 
658 #endif
Definition: gc_ptr.h:151
gc_ptr & operator=(gc_ptr &&o)
Definition: gc_ptr.h:202
void reset()
Definition: gc_ptr.h:233
T * ptr
Definition: gc_ptr.h:154
gc_base_ptr(gc_object *c=nullptr)
bool mark
Definition: gc_ptr.h:89
gc_ptr & operator=(nullptr_t)
Definition: gc_ptr.h:209
gc_ptr(gc_ptr &&o)
Definition: gc_ptr.h:172
make_gc(Args &&...args)
Definition: gc_ptr.h:264
Definition: gc_ptr.h:257
long gc_counter
gc_ptr< T > operator+(const gc_ptr< T > &a, const U &b) noexcept
Definition: gc_ptr.h:573
std::atomic< gc_object * > object
Definition: gc_ptr.h:121
Definition: gc_ptr.h:116
Definition: gc_ptr.h:52
void * end() noexcept
Definition: gc_ptr.h:75
gc_ptr(nullptr_t)
Definition: gc_ptr.h:162
T & operator[](int idx) const noexcept
Definition: gc_ptr.h:230
gc_ptr< T > & operator+=(const U &z) noexcept
Definition: gc_ptr.h:249
std::atomic< gc_base_ptr * > first
Definition: gc_ptr.h:86
gc_base_ptr(const gc_base_ptr &o)
Definition: gc_ptr.h:128
gc_ptr< T > reinterpret_pointer_cast(const gc_ptr< U > &p) noexcept
Definition: gc_ptr.h:641
std::atomic< int > root_ref_cnt
Definition: gc_ptr.h:83
gc_ptr< T > operator++(int) noexcept
Definition: gc_ptr.h:242
gc_ptr< T > & operator++() noexcept
Definition: gc_ptr.h:236
std::atomic< gc_base_ptr * > next
Definition: gc_ptr.h:120
make_gc(unsigned size)
Definition: gc_ptr.h:325
gc_ptr< T > pointer_cast(const gc_ptr< U > &p) noexcept
Definition: gc_ptr.h:649
T * get() const noexcept
Definition: gc_ptr.h:218
void * end_
Definition: gc_ptr.h:63
bool operator>=(const gc_ptr< T > &lhs, const gc_ptr< T > &rhs) noexcept
Definition: gc_ptr.h:542
T * operator->() const noexcept
Definition: gc_ptr.h:227
thread_local gc_object * current
gc_ptr< T > & operator-=(const U &z) noexcept
Definition: gc_ptr.h:252
gc_ptr(const gc_ptr &o)
Definition: gc_ptr.h:167
bool operator!=(const gc_ptr< T > &lhs, const gc_ptr< U > &rhs) noexcept
Definition: gc_ptr.h:422
std::vector< gc_object * > all_objects
bool operator==(const gc_ptr< T > &lhs, const gc_ptr< U > &rhs) noexcept
Definition: gc_ptr.h:392
gc_ptr< T > static_pointer_cast(const gc_ptr< U > &p) noexcept
Definition: gc_ptr.h:617
std::mutex gc_mutex
gc_ptr & operator=(const gc_ptr &o)
Definition: gc_ptr.h:193
gc_ptr< T > operator-(const gc_ptr< T > &a, const U &b) noexcept
Definition: gc_ptr.h:593
enum gc_base_ptr::PtrType type
void reset()
bool operator>(const gc_ptr< T > &lhs, const gc_ptr< T > &rhs) noexcept
Definition: gc_ptr.h:512
PtrType
Definition: gc_ptr.h:115
gc_ptr(const gc_base_ptr &o, T *p)
Definition: gc_ptr.h:179
void * start() noexcept
Definition: gc_ptr.h:70
void gc_collect()
gc_ptr< T > const_pointer_cast(const gc_ptr< U > &p) noexcept
Definition: gc_ptr.h:633
gc_ptr(const gc_ptr< U > &o)
Definition: gc_ptr.h:185
gc_ptr()
Definition: gc_ptr.h:159
gc_ptr< T > & operator--() noexcept
Definition: gc_ptr.h:239
Definition: gc_ptr.h:117
void operator=(nullptr_t)
T & operator*() const noexcept
Definition: gc_ptr.h:224
gc_ptr< T > dynamic_pointer_cast(const gc_ptr< U > &p) noexcept
Definition: gc_ptr.h:625
Definition: gc_ptr.h:107
gc_ptr< T > operator--(int) noexcept
Definition: gc_ptr.h:245