1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 
3 /*
4  *  Main authors:
5  *     Guido Tack <guido.tack@monash.edu>
6  */
7 
8 /* This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11 
12 #pragma once
13 
14 #include <minizinc/config.hh>
15 #include <minizinc/timer.hh>
16 
17 #include <cassert>
18 #include <cstdlib>
19 #include <new>
20 #include <unordered_map>
21 
22 //#define MINIZINC_GC_STATS
23 
24 #if defined(MINIZINC_GC_STATS)
25 #include <map>
26 
27 struct GCStat {
28   int first;
29   int second;
30   int keepalive;
31   int inmodel;
32   size_t total;
GCStatGCStat33   GCStat() : first(0), second(0), keepalive(0), inmodel(0), total(0) {}
34 };
35 
36 #define MINIZINC_GC_STAT_ARGS std::map<int, GCStat>& gc_stats
37 #else
38 
39 #define MINIZINC_GC_STAT_ARGS
40 
41 #endif
42 
43 namespace MiniZinc {
44 
45 /**
46  * \brief Base class for abstract syntax tree nodes
47  */
48 class ASTNode {
49   friend class GC;
50 
51 protected:
52   /// Mark for garbage collection
53   mutable unsigned int _gcMark : 1;
54   /// Id of the node
55   unsigned int _id : 7;
56   /// Secondary id
57   unsigned int _secondaryId : 7;
58   /// Flag
59   unsigned int _flag1 : 1;
60   /// Flag
61   unsigned int _flag2 : 1;
62 
63   enum BaseNodes { NID_FL, NID_CHUNK, NID_VEC, NID_STR, NID_END = NID_STR };
64 
65   /// Constructor
ASTNode(unsigned int id)66   ASTNode(unsigned int id) : _gcMark(0), _id(id) {}
67 
68 public:
69   /// Allocate node
70   void* operator new(size_t size);
71 
72   /// Placement-new
operator new(size_t,void * n)73   void* operator new(size_t /*s*/, void* n) throw() { return n; }
74 
75   /// Delete node (no-op)
operator delete(void *,size_t)76   void operator delete(void* /*n*/, size_t /*s*/) throw() {}
77   /// Delete node (no-op)
operator delete(void *,void *)78   void operator delete(void* /*n*/, void* /*m*/) throw() {}
79 
80   /// Delete node (no-op)
operator delete(void *)81   void operator delete(void* /*n*/) throw() {}
82 };
83 
84 /**
85  * \brief Base class for unstructured garbage collected data
86  */
87 class ASTChunk : public ASTNode {
88   friend class GC;
89 
90 protected:
91   /// Allocated size
92   size_t _size;
93   /// Storage
94   char _data[4];
95   /// Constructor
96   ASTChunk(size_t size, unsigned int id = ASTNode::NID_CHUNK);
97   /// Actual size of object in memory
memsize() const98   size_t memsize() const {
99     size_t s = sizeof(ASTChunk) + (_size <= 4 ? 0 : _size - 4) * sizeof(char);
100     s += ((8 - (s & 7)) & 7);
101     return s;
102   }
103   /// Allocate raw memory
104   static void* alloc(size_t size);
105 };
106 
107 /**
108  * \brief Base class for structured garbage collected data
109  */
110 class ASTVec : public ASTNode {
111   friend class GC;
112 
113 protected:
114   /// Allocated size
115   size_t _size;
116   /// Storage
117   void* _data[2];
118   /// Constructor
119   ASTVec(size_t size);
120   /// Actual size of object in memory
memsize() const121   size_t memsize() const {
122     size_t s = sizeof(ASTVec) + (_size <= 2 ? 0 : _size - 2) * sizeof(void*);
123     s += ((8 - (s & 7)) & 7);
124     return s;
125   }
126   /// Allocate raw memory
127   static void* alloc(size_t size);
128 };
129 
130 class Expression;
131 
132 class GCMarker;
133 class KeepAlive;
134 class WeakRef;
135 
136 class ASTNodeWeakMap;
137 class ASTStringData;
138 
139 /// Garbage collector
140 class GC {
141   friend class ASTNode;
142   friend class ASTVec;
143   friend class ASTChunk;
144   friend class ASTStringData;
145   friend class KeepAlive;
146   friend class WeakRef;
147   friend class ASTNodeWeakMap;
148 
149 private:
150   class Heap;
151   /// The memory controlled by the collector
152   Heap* _heap;
153   /// Count how many locks are currently active
154   unsigned int _lockCount;
155   /// Timeout in milliseconds
156   unsigned long long int _timeout;
157   /// Counter for timeout
158   int _timeoutCount;
159   /// Timer for timeout
160   Timer _timeoutTimer;
161   /// Return thread-local GC object
162   static GC*& gc();
163   /// Constructor
164   GC();
165 
166   /// Allocate garbage collected memory
167   void* alloc(size_t size);
168 
169   static void addKeepAlive(KeepAlive* e);
170   static void removeKeepAlive(KeepAlive* e);
171   static void addWeakRef(WeakRef* e);
172   static void removeWeakRef(WeakRef* e);
173   static void addNodeWeakMap(ASTNodeWeakMap* m);
174   static void removeNodeWeakMap(ASTNodeWeakMap* m);
175 
176 public:
177   /// Acquire garbage collector lock for this thread
178   static void lock();
179   /// Release garbage collector lock for this thread
180   static void unlock();
181   /// Manually trigger garbage collector (must be unlocked)
182   static void trigger();
183   /// Test if garbage collector is locked
184   static bool locked();
185   /// Add model \a m to root set
186   static void add(GCMarker* m);
187   /// Remove model \a m from root set
188   static void remove(GCMarker* m);
189 
190   /// Put a mark on the trail
191   static void mark();
192   /// Add a trail entry
193   static void trail(Expression** l, Expression* v);
194   /// Untrail to previous mark
195   static void untrail();
196 
197   /// Set timeout of \a t milliseconds, 0 means disable
198   static void setTimeout(unsigned long long int t);
199 
200   /// Return maximum allocated memory (high water mark)
201   static size_t maxMem();
202 };
203 
204 /// Automatic garbage collection lock
205 class GCLock {
206 public:
207   /// Acquire lock
208   GCLock();
209   /// Release lock upon destruction
210   ~GCLock();
211 };
212 
213 /// Expression wrapper that is a member of the root set
214 class KeepAlive {
215   friend class GC;
216 
217 private:
218   Expression* _e;
219   KeepAlive* _p;
220   KeepAlive* _n;
221 
222 public:
223   KeepAlive(Expression* e = nullptr);
224   ~KeepAlive();
225   KeepAlive(const KeepAlive& e);
226   KeepAlive& operator=(const KeepAlive& e);
operator ()()227   Expression* operator()() { return _e; }
operator ()() const228   Expression* operator()() const { return _e; }
next() const229   KeepAlive* next() const { return _n; }
230 };
231 
232 /// Expression wrapper that is a member of the root set
233 class WeakRef {
234   friend class GC;
235 
236 private:
237   Expression* _e;
238   WeakRef* _p;
239   WeakRef* _n;
240   bool _valid;
241 
242 public:
243   WeakRef(Expression* e = nullptr);
244   ~WeakRef();
245   WeakRef(const WeakRef& e);
246   WeakRef& operator=(const WeakRef& e);
operator ()()247   Expression* operator()() { return _valid ? _e : nullptr; }
operator ()() const248   Expression* operator()() const { return _valid ? _e : nullptr; }
next() const249   WeakRef* next() const { return _n; }
250 };
251 
252 class ASTNodeWeakMap {
253   friend class GC;
254 
255 private:
256   ASTNodeWeakMap(const WeakRef& e);
257   ASTNodeWeakMap& operator=(const ASTNodeWeakMap& e);
258 
259 protected:
260   typedef std::unordered_map<ASTNode*, ASTNode*> NodeMap;
261   ASTNodeWeakMap* _p;
262   ASTNodeWeakMap* _n;
next() const263   ASTNodeWeakMap* next() const { return _n; }
264   NodeMap _m;
265 
266 public:
267   ASTNodeWeakMap();
268   ~ASTNodeWeakMap();
269   void insert(ASTNode* n0, ASTNode* n1);
270   ASTNode* find(ASTNode* n);
clear()271   void clear() { _m.clear(); }
272 };
273 
274 /**
275  * \brief Abstract base class for object containing garbage collected data
276  */
277 class GCMarker {
278   friend class GC;
279 
280 private:
281   /// Previous object in root set list
282   GCMarker* _rootsPrev = nullptr;
283   /// Next object in root set list
284   GCMarker* _rootsNext = nullptr;
285 
286 protected:
287   /// Mark garbage collected objects that
288   virtual void mark(MINIZINC_GC_STAT_ARGS) = 0;
289 
290 public:
GCMarker()291   GCMarker() { GC::add(this); }
~GCMarker()292   virtual ~GCMarker() { GC::remove(this); }
293 };
294 
295 }  // namespace MiniZinc
296