1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Clark Barrett, Morgan Deters, Tim King |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 by the authors listed in the file AUTHORS |
8 |
|
* in the top-level source directory and their institutional affiliations. |
9 |
|
* All rights reserved. See the file COPYING in the top-level source |
10 |
|
* directory for licensing information. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Context class and context manager. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__CONTEXT__CONTEXT_H |
19 |
|
#define CVC5__CONTEXT__CONTEXT_H |
20 |
|
|
21 |
|
#include <cstdlib> |
22 |
|
#include <iostream> |
23 |
|
#include <memory> |
24 |
|
#include <typeinfo> |
25 |
|
#include <vector> |
26 |
|
|
27 |
|
#include "base/check.h" |
28 |
|
#include "base/output.h" |
29 |
|
#include "context/context_mm.h" |
30 |
|
|
31 |
|
namespace cvc5 { |
32 |
|
namespace context { |
33 |
|
|
34 |
|
class Context; |
35 |
|
class Scope; |
36 |
|
class ContextObj; |
37 |
|
class ContextNotifyObj; |
38 |
|
|
39 |
|
/** Pretty-printing of Contexts (for debugging) */ |
40 |
|
std::ostream& operator<<(std::ostream&, const Context&); |
41 |
|
|
42 |
|
/** Pretty-printing of Scopes (for debugging) */ |
43 |
|
std::ostream& operator<<(std::ostream&, const Scope&); |
44 |
|
|
45 |
|
/** |
46 |
|
* A Context encapsulates all of the dynamic state of the system. Its main |
47 |
|
* methods are push() and pop(). A call to push() saves the current state, |
48 |
|
* and a call to pop() restores the state saved by the most recent call to |
49 |
|
* push(). |
50 |
|
* |
51 |
|
* Objects which want to participate in this global save and restore |
52 |
|
* mechanism must inherit from ContextObj (see below). |
53 |
|
* |
54 |
|
* For more flexible context-dependent behavior, objects may implement the |
55 |
|
* ContextNotifyObj interface and simply get a notification when a pop has |
56 |
|
* occurred. |
57 |
|
* |
58 |
|
* Context also uses a helper class called Scope which stores information |
59 |
|
* specific to the portion of the Context since the last call to push() (see |
60 |
|
* below). |
61 |
|
* |
62 |
|
* Memory allocation in Contexts is done with the help of the |
63 |
|
* ContextMemoryManager. A copy is stored in each Scope object for quick |
64 |
|
* access. |
65 |
|
*/ |
66 |
|
class Context { |
67 |
|
|
68 |
|
/** |
69 |
|
* Pointer to the ContextMemoryManager for this Context. |
70 |
|
*/ |
71 |
|
ContextMemoryManager* d_pCMM; |
72 |
|
|
73 |
|
/** |
74 |
|
* List of all scopes for this context. |
75 |
|
*/ |
76 |
|
std::vector<Scope*> d_scopeList; |
77 |
|
|
78 |
|
/** |
79 |
|
* Doubly-linked list of objects to notify before every pop. See |
80 |
|
* ContextNotifyObj for structure of linked list. |
81 |
|
*/ |
82 |
|
ContextNotifyObj* d_pCNOpre; |
83 |
|
|
84 |
|
/** |
85 |
|
* Doubly-linked list of objects to notify after every pop. See |
86 |
|
* ContextNotifyObj for structure of linked list. |
87 |
|
*/ |
88 |
|
ContextNotifyObj* d_pCNOpost; |
89 |
|
|
90 |
|
friend std::ostream& operator<<(std::ostream&, const Context&); |
91 |
|
|
92 |
|
// disable copy, assignment |
93 |
|
Context(const Context&) = delete; |
94 |
|
Context& operator=(const Context&) = delete; |
95 |
|
|
96 |
|
public: |
97 |
|
|
98 |
|
/** |
99 |
|
* A mechanism by which a "scoped" bit of contextual speculation can |
100 |
|
* be applied. One might create a Context::ScopedPush in a function |
101 |
|
* (as a local variable on the stack), then manipulate some |
102 |
|
* context-dependent data structures in some fashion, speculatively. |
103 |
|
* When the ScopedPush goes out of scope and is destructed, the |
104 |
|
* context-dependent data structures will return to their original |
105 |
|
* state. |
106 |
|
* |
107 |
|
* When such speculation occurs in a lexically-scoped manner, like |
108 |
|
* described above, it is FAR preferable to use ScopedPush than to |
109 |
|
* call ->push() and ->pop() on the Context directly. If you do the |
110 |
|
* latter, it's extremely easy to forget to pop() on exceptional |
111 |
|
* exit of the function, or if a short-circuited "early" return is |
112 |
|
* later added to the function, etc. Further, ScopedPush includes |
113 |
|
* an assertion that the Context at the end looks like the Context |
114 |
|
* at the beginning (the topmost Scope pointer should be the same). |
115 |
|
* This assertion is only an approximate check for correct behavior, |
116 |
|
* but should catch egregious mismatches of ->push() and ->pop() |
117 |
|
* while the ScopedPush is being applied---egregious mismatches that |
118 |
|
* could exist, for example, if a Theory does some speculative |
119 |
|
* reasoning but accidently gives control back to some other mechanism |
120 |
|
* which does some speculation which isn't properly scoped inside the |
121 |
|
* first. |
122 |
|
*/ |
123 |
|
class ScopedPush { |
124 |
|
Context* const d_context; |
125 |
|
const Scope* const d_scope; |
126 |
|
public: |
127 |
1656 |
ScopedPush(Context* ctxt) : |
128 |
|
d_context(ctxt), |
129 |
1656 |
d_scope(d_context->getTopScope()) { |
130 |
1656 |
d_context->push(); |
131 |
1656 |
} |
132 |
3312 |
~ScopedPush() noexcept(false) { |
133 |
1656 |
d_context->pop(); |
134 |
1656 |
AlwaysAssert(d_context->getTopScope() == d_scope) |
135 |
|
<< "Context::ScopedPush observed an uneven Context (at pop, " |
136 |
|
"top scope doesn't match what it was at the time the " |
137 |
|
"ScopedPush was applied)"; |
138 |
1656 |
} |
139 |
|
};/* Context::ScopedPush */ |
140 |
|
|
141 |
|
/** |
142 |
|
* Constructor: create ContextMemoryManager and initial Scope |
143 |
|
*/ |
144 |
|
Context(); |
145 |
|
|
146 |
|
/** |
147 |
|
* Destructor: pop all scopes, delete ContextMemoryManager |
148 |
|
*/ |
149 |
|
~Context(); |
150 |
|
|
151 |
|
/** |
152 |
|
* Return the current (top) scope |
153 |
|
*/ |
154 |
429527413 |
Scope* getTopScope() const { return d_scopeList.back(); } |
155 |
|
|
156 |
|
/** |
157 |
|
* Return the initial (bottom) scope |
158 |
|
*/ |
159 |
64634166 |
Scope* getBottomScope() const { return d_scopeList[0]; } |
160 |
|
|
161 |
|
/** |
162 |
|
* Return the current Scope level. |
163 |
|
*/ |
164 |
472802621 |
int getLevel() const { return d_scopeList.size() - 1; } |
165 |
|
|
166 |
|
/** |
167 |
|
* Return the ContextMemoryManager associated with the context. |
168 |
|
*/ |
169 |
|
ContextMemoryManager* getCMM() { return d_pCMM; } |
170 |
|
|
171 |
|
/** |
172 |
|
* Save the current state, create a new Scope |
173 |
|
*/ |
174 |
|
void push(); |
175 |
|
|
176 |
|
/** |
177 |
|
* Restore the previous state, delete the top Scope |
178 |
|
*/ |
179 |
|
void pop(); |
180 |
|
|
181 |
|
/** |
182 |
|
* Pop all the way back to given level |
183 |
|
*/ |
184 |
|
void popto(int toLevel); |
185 |
|
|
186 |
|
/** |
187 |
|
* Add pCNO to the list of objects notified before every pop |
188 |
|
*/ |
189 |
|
void addNotifyObjPre(ContextNotifyObj* pCNO); |
190 |
|
|
191 |
|
/** |
192 |
|
* Add pCNO to the list of objects notified after every pop |
193 |
|
*/ |
194 |
|
void addNotifyObjPost(ContextNotifyObj* pCNO); |
195 |
|
|
196 |
|
};/* class Context */ |
197 |
|
|
198 |
|
|
199 |
|
/** |
200 |
|
* A UserContext is different from a Context only because it's used for |
201 |
|
* different purposes---so separating the two types gives type errors where |
202 |
|
* appropriate. |
203 |
|
*/ |
204 |
9647 |
class UserContext : public Context { |
205 |
|
private: |
206 |
|
// disable copy, assignment |
207 |
|
UserContext(const UserContext&) = delete; |
208 |
|
UserContext& operator=(const UserContext&) = delete; |
209 |
|
public: |
210 |
9647 |
UserContext() {} |
211 |
|
};/* class UserContext */ |
212 |
|
|
213 |
|
|
214 |
|
/** |
215 |
|
* Conceptually, a Scope encapsulates that portion of the context that |
216 |
|
* changes after a call to push() and must be undone on a subsequent call to |
217 |
|
* pop(). In particular, each call to push() creates a new Scope object . |
218 |
|
* This new Scope object becomes the top scope and it points (via the |
219 |
|
* d_pScopePrev member) to the previous top Scope. Each call to pop() |
220 |
|
* deletes the current top scope and restores the previous one. The main |
221 |
|
* purpose of a Scope is to maintain a linked list of ContexObj objects which |
222 |
|
* change while the Scope is the top scope and which must be restored when |
223 |
|
* the Scope is deleted. |
224 |
|
* |
225 |
|
* A Scope is also associated with a ContextMemoryManager. All memory |
226 |
|
* allocated by the Scope is allocated in a single region using the |
227 |
|
* ContextMemoryManager and released all at once when the Scope is popped. |
228 |
|
*/ |
229 |
|
class Scope { |
230 |
|
|
231 |
|
/** |
232 |
|
* Context that created this Scope |
233 |
|
*/ |
234 |
|
Context* d_pContext; |
235 |
|
|
236 |
|
/** |
237 |
|
* Memory manager for this Scope. Same as in Context, but stored here too |
238 |
|
* for faster access by ContextObj objects. |
239 |
|
*/ |
240 |
|
ContextMemoryManager* d_pCMM; |
241 |
|
|
242 |
|
/** |
243 |
|
* Scope level (total number of outstanding push() calls when this Scope was |
244 |
|
* created). |
245 |
|
*/ |
246 |
|
int d_level; |
247 |
|
|
248 |
|
/** |
249 |
|
* Linked list of objects which changed in this scope, |
250 |
|
* and thus need to be restored when the scope is deleted. |
251 |
|
*/ |
252 |
|
ContextObj* d_pContextObjList; |
253 |
|
|
254 |
|
/** |
255 |
|
* A list of ContextObj to be garbage collected before the destruction of this |
256 |
|
* scope. deleteSelf() will be called on each element during ~Scope(). |
257 |
|
* |
258 |
|
* This is either nullptr or list owned by this scope. |
259 |
|
*/ |
260 |
|
std::unique_ptr<std::vector<ContextObj*>> d_garbage; |
261 |
|
|
262 |
|
friend std::ostream& operator<<(std::ostream&, const Scope&); |
263 |
|
|
264 |
|
public: |
265 |
|
/** |
266 |
|
* Constructor: Create a new Scope; set the level and the previous Scope |
267 |
|
* if any. |
268 |
|
*/ |
269 |
4650775 |
Scope(Context* pContext, ContextMemoryManager* pCMM, int level) |
270 |
4650775 |
: d_pContext(pContext), |
271 |
|
d_pCMM(pCMM), |
272 |
|
d_level(level), |
273 |
|
d_pContextObjList(nullptr), |
274 |
4650775 |
d_garbage() |
275 |
|
{ |
276 |
4650775 |
} |
277 |
|
|
278 |
|
/** |
279 |
|
* Destructor: Clears out all of the garbage and restore all of the objects |
280 |
|
* in ContextObjList. |
281 |
|
*/ |
282 |
|
~Scope(); |
283 |
|
|
284 |
|
/** |
285 |
|
* Get the Context for this Scope |
286 |
|
*/ |
287 |
1025169513 |
Context* getContext() const { return d_pContext; } |
288 |
|
|
289 |
|
/** |
290 |
|
* Get the ContextMemoryManager for this Scope |
291 |
|
*/ |
292 |
79822210 |
ContextMemoryManager* getCMM() const { return d_pCMM; } |
293 |
|
|
294 |
|
/** |
295 |
|
* Get the level of the current Scope |
296 |
|
*/ |
297 |
64686377 |
int getLevel() const { return d_level; } |
298 |
|
|
299 |
|
/** |
300 |
|
* Return true iff this Scope is the current top Scope |
301 |
|
*/ |
302 |
349702118 |
bool isCurrent() const { return this == d_pContext->getTopScope(); } |
303 |
|
|
304 |
|
/** |
305 |
|
* When a ContextObj object is modified for the first time in this |
306 |
|
* Scope, it should call this method to add itself to the list of |
307 |
|
* objects that will need to be restored. Defined inline below. |
308 |
|
*/ |
309 |
|
void addToChain(ContextObj* pContextObj); |
310 |
|
|
311 |
|
/** |
312 |
|
* Overload operator new for use with ContextMemoryManager to allow |
313 |
|
* creation of new Scope objects in the current memory region. |
314 |
|
*/ |
315 |
4650775 |
static void* operator new(size_t size, ContextMemoryManager* pCMM) |
316 |
|
{ |
317 |
4650775 |
Trace("context_mm") << "Scope::new " << size << " in " << pCMM << std::endl; |
318 |
4650775 |
return pCMM->newData(size); |
319 |
|
} |
320 |
|
|
321 |
|
/** |
322 |
|
* Enqueues a ContextObj to be garbage collected via a call to deleteSelf() |
323 |
|
* during the destruction of this scope. |
324 |
|
*/ |
325 |
|
void enqueueToGarbageCollect(ContextObj* obj); |
326 |
|
|
327 |
|
/** |
328 |
|
* Overload operator delete for Scope objects allocated using |
329 |
|
* ContextMemoryManager. No need to do anything because memory is |
330 |
|
* freed automatically when the ContextMemoryManager pop() method is |
331 |
|
* called. Include both placement and standard delete for |
332 |
|
* completeness. |
333 |
|
*/ |
334 |
|
static void operator delete(void* pMem, ContextMemoryManager* pCMM) {} |
335 |
2536394 |
static void operator delete(void* pMem) {} |
336 |
|
|
337 |
|
//FIXME: //! Check for memory leaks |
338 |
|
// void check(); |
339 |
|
|
340 |
|
};/* class Scope */ |
341 |
|
|
342 |
|
/** |
343 |
|
* This is an abstract base class from which all objects that are |
344 |
|
* context-dependent should inherit. For any data structure that you |
345 |
|
* want to have be automatically backtracked, do the following: |
346 |
|
* |
347 |
|
* 1. Create a class for the data structure that inherits from ContextObj |
348 |
|
* |
349 |
|
* 2. Implement save() |
350 |
|
* The role of save() is to create a new ContexObj object that contains |
351 |
|
* enough information to restore the object to its current state, even if |
352 |
|
* it gets changed later. Note that save should call the (default) |
353 |
|
* ContextObj Copy Constructor to copy the information in the base class. |
354 |
|
* It is recommended that any memory allocated by save() should be done |
355 |
|
* through the ContextMemoryManager. This way, it does not need to be |
356 |
|
* explicitly freed when restore is called. However, it is only required |
357 |
|
* that the ContextObj itself be allocated using the |
358 |
|
* ContextMemoryManager. If other memory is allocated not using the |
359 |
|
* ContextMemoryManager, it should be freed when restore() is called. In |
360 |
|
* fact, any clean-up work on a saved object must be done by restore(). |
361 |
|
* This is because the destructor is never called explicitly on saved |
362 |
|
* objects. |
363 |
|
* |
364 |
|
* 3. Implement restore() |
365 |
|
* The role of restore() is, given the ContextObj returned by a previous |
366 |
|
* call to save(), to restore the current object to the state it was in |
367 |
|
* when save() was called. Note that the implementation of restore does |
368 |
|
* *not* need to worry about restoring the base class data. This is done |
369 |
|
* automatically. Ideally, restore() should not have to free any memory |
370 |
|
* as any memory allocated by save() should have been done using the |
371 |
|
* ContextMemoryManager (see item 2 above). |
372 |
|
* |
373 |
|
* 4. In the subclass implementation, any time the state is about to be |
374 |
|
* changed, first call makeCurrent(). |
375 |
|
* |
376 |
|
* 5. In the subclass implementation, the destructor should call destroy(), |
377 |
|
* which repeatedly calls restore() until the object is restored to context |
378 |
|
* level 0. Note, however, that if there is additional cleanup required at |
379 |
|
* level 0, destroy() does not do this. It has to be implemented in the |
380 |
|
* destructor of the subclass. The reason the destroy() functionality |
381 |
|
* cannot be in the ContextObj destructor is that it needs to call the |
382 |
|
* subclass-specific restore() method in order to properly clean up saved |
383 |
|
* copies. |
384 |
|
* |
385 |
|
* GOTCHAS WHEN ALLOCATING CONTEXTUAL OBJECTS WITH NON-STANDARD ALLOCATORS |
386 |
|
* |
387 |
|
* Be careful if you intend to allocate ContextObj in (for example) |
388 |
|
* ContextMemoryManager memory. ContextMemoryManager doesn't call the |
389 |
|
* destructors of contained objects, which means the objects aren't |
390 |
|
* properly unregistered from the Context (as in point #5, above). |
391 |
|
* This can be remedied by allocating the ContextObj in the "top |
392 |
|
* scope" rather than the "bottom scope" (which is what the |
393 |
|
* "allocatedInCMM" flag to the special constructor in ContextObj |
394 |
|
* does). |
395 |
|
* |
396 |
|
* But why allocate in CMM if it's so complicated? There's a big |
397 |
|
* advantage: you don't have to track the lifetime of the ContextObj. |
398 |
|
* The object simply ceases to exist when the Context is popped. Thus |
399 |
|
* you can create an object in context memory, and if you stow |
400 |
|
* pointers to it only in context memory as well, all is cleaned up |
401 |
|
* for you when the scope pops. Here's a list of gotchas: |
402 |
|
* |
403 |
|
* 1. For data structures intended solely to be allocated in context |
404 |
|
* memory, privately declare (but don't define) an operator |
405 |
|
* new(size_t) and destructor (as currently in the Link class, in |
406 |
|
* src/theory/uf/ecdata.h). |
407 |
|
* |
408 |
|
* 2. For data structures that may or may not be allocated in context |
409 |
|
* memory, and are designed to be that way (esp. if they contain |
410 |
|
* ContextObj instances), they should be heavily documented -- |
411 |
|
* especially the destructor, since it _may_or_may_not_be_called_. |
412 |
|
* |
413 |
|
* 3. There's also an issue for generic code -- some class Foo<T> |
414 |
|
* might be allocated in context memory, and that might normally be |
415 |
|
* fine, but if T is a ContextObj this requires certain care. |
416 |
|
* |
417 |
|
* 4. Note that certain care is required for ContextObj inside of data |
418 |
|
* structures. If the (enclosing) data structure can be allocated |
419 |
|
* in CMM, that means the (enclosed) ContextObj can be, even if it |
420 |
|
* was not designed to be allocated in that way. In many |
421 |
|
* instances, it may be required that data structures enclosing |
422 |
|
* ContextObj be parameterized with a similar "bool allocatedInCMM" |
423 |
|
* argument as the special constructor in this class (and pass it |
424 |
|
* on to all ContextObj instances). |
425 |
|
*/ |
426 |
79821977 |
class ContextObj { |
427 |
|
/** |
428 |
|
* Pointer to Scope in which this object was last modified. |
429 |
|
*/ |
430 |
|
Scope* d_pScope; |
431 |
|
|
432 |
|
/** |
433 |
|
* Pointer to most recent version of same ContextObj in a previous Scope |
434 |
|
*/ |
435 |
|
ContextObj* d_pContextObjRestore; |
436 |
|
|
437 |
|
/** |
438 |
|
* Next link in ContextObjList list maintained by Scope class. |
439 |
|
*/ |
440 |
|
ContextObj* d_pContextObjNext; |
441 |
|
|
442 |
|
/** |
443 |
|
* Previous link in ContextObjList list maintained by Scope class. We use |
444 |
|
* double-indirection here to make element deletion easy. |
445 |
|
*/ |
446 |
|
ContextObj** d_ppContextObjPrev; |
447 |
|
|
448 |
|
/** |
449 |
|
* Helper method for makeCurrent (see below). Separated out to allow common |
450 |
|
* case to be inlined without making a function call. It calls save() and |
451 |
|
* does the necessary bookkeeping to ensure that object can be restored to |
452 |
|
* its current state when restore is called. |
453 |
|
*/ |
454 |
|
void update(); |
455 |
|
|
456 |
|
// The rest of the private methods are for the benefit of the Scope. We make |
457 |
|
// Scope our friend so it is the only one that can use them. |
458 |
|
|
459 |
|
friend class Scope; |
460 |
|
|
461 |
|
friend std::ostream& operator<<(std::ostream&, const Scope&); |
462 |
|
|
463 |
|
/** |
464 |
|
* Return reference to next link in ContextObjList. Used by |
465 |
|
* Scope::addToChain method. |
466 |
|
*/ |
467 |
1259941782 |
ContextObj*& next() { return d_pContextObjNext; } |
468 |
|
|
469 |
|
/** |
470 |
|
* Return reference to prev link in ContextObjList. Used by |
471 |
|
* Scope::addToChain method. |
472 |
|
*/ |
473 |
1259218519 |
ContextObj**& prev() { return d_ppContextObjPrev; } |
474 |
|
|
475 |
|
/** |
476 |
|
* This method is called by Scope during a pop: it does the necessary work to |
477 |
|
* restore the object from its saved copy and then returns the next object in |
478 |
|
* the list that needs to be restored. |
479 |
|
*/ |
480 |
|
ContextObj* restoreAndContinue(); |
481 |
|
|
482 |
|
protected: |
483 |
|
/** |
484 |
|
* This is a method that must be implemented by all classes inheriting from |
485 |
|
* ContextObj. See the comment before the class declaration. |
486 |
|
*/ |
487 |
|
virtual ContextObj* save(ContextMemoryManager* pCMM) = 0; |
488 |
|
|
489 |
|
/** |
490 |
|
* This is a method that must be implemented by all classes inheriting from |
491 |
|
* ContextObj. See the comment before the class declaration. |
492 |
|
*/ |
493 |
|
virtual void restore(ContextObj* pContextObjRestore) = 0; |
494 |
|
|
495 |
|
/** |
496 |
|
* This method checks if the object has been modified in this Scope |
497 |
|
* yet. If not, it calls update(). |
498 |
|
*/ |
499 |
|
inline void makeCurrent(); |
500 |
|
|
501 |
|
/** |
502 |
|
* Just calls update(), but given a different name for the derived |
503 |
|
* class-facing interface. This is a "forced" makeCurrent(), useful |
504 |
|
* for ContextObjs allocated in CMM that need a special "bottom" |
505 |
|
* case when they disappear out of existence (kind of a destructor). |
506 |
|
* See CDOhash_map (in cdhashmap.h) for an example. |
507 |
|
*/ |
508 |
|
inline void makeSaveRestorePoint(); |
509 |
|
|
510 |
|
/** |
511 |
|
* Should be called from sub-class destructor: calls restore until restored |
512 |
|
* to initial version (version at context level 0). Also removes object from |
513 |
|
* all Scope lists. Note that this doesn't actually free the memory |
514 |
|
* allocated by the ContextMemoryManager for this object. This isn't done |
515 |
|
* until the corresponding Scope is popped. |
516 |
|
*/ |
517 |
|
void destroy(); |
518 |
|
|
519 |
|
///// |
520 |
|
// |
521 |
|
// These next four accessors return properties of the Scope to |
522 |
|
// derived classes without giving them the Scope object directly. |
523 |
|
// |
524 |
|
///// |
525 |
|
|
526 |
|
/** |
527 |
|
* Get the Context with which this ContextObj was created. This is |
528 |
|
* part of the protected interface, intended for derived classes to |
529 |
|
* use if necessary. |
530 |
|
*/ |
531 |
945347536 |
Context* getContext() const { return d_pScope->getContext(); } |
532 |
|
|
533 |
|
/** |
534 |
|
* Get the ContextMemoryManager with which this ContextObj was |
535 |
|
* created. This is part of the protected interface, intended for |
536 |
|
* derived classes to use if necessary. If a ContextObj-derived |
537 |
|
* class needs to allocate memory somewhere other than the save() |
538 |
|
* member function (where it is explicitly given a |
539 |
|
* ContextMemoryManager), it can use this accessor to get the memory |
540 |
|
* manager. |
541 |
|
*/ |
542 |
|
ContextMemoryManager* getCMM() const { return d_pScope->getCMM(); } |
543 |
|
|
544 |
|
/** |
545 |
|
* Get the level associated to this ContextObj. Useful if a |
546 |
|
* ContextObj-derived class needs to compare the level of its last |
547 |
|
* update with another ContextObj. |
548 |
|
*/ |
549 |
64686377 |
int getLevel() const { return d_pScope->getLevel(); } |
550 |
|
|
551 |
|
/** |
552 |
|
* Returns true if the object is "current"-- that is, updated in the |
553 |
|
* topmost contextual scope. Useful if a ContextObj-derived class |
554 |
|
* needs to determine if it has been modified in the current scope. |
555 |
|
* Note that it is always safe to call makeCurrent() without first |
556 |
|
* checking if the object is current, so this function need not be |
557 |
|
* used under normal circumstances. |
558 |
|
*/ |
559 |
|
bool isCurrent() const { return d_pScope->isCurrent(); } |
560 |
|
|
561 |
|
public: |
562 |
|
/** |
563 |
|
* Disable delete: objects allocated with new(ContextMemorymanager) should |
564 |
|
* never be deleted. Objects allocated with new(bool) should be deleted by |
565 |
|
* calling deleteSelf(). |
566 |
|
*/ |
567 |
|
static void operator delete(void* pMem) { |
568 |
|
AlwaysAssert(false) << "It is not allowed to delete a ContextObj this way!"; |
569 |
|
} |
570 |
|
|
571 |
|
/** |
572 |
|
* operator new using ContextMemoryManager (common case used by |
573 |
|
* subclasses during save()). No delete is required for memory |
574 |
|
* allocated this way, since it is automatically released when the |
575 |
|
* context is popped. Also note that allocations using this |
576 |
|
* operator never have their destructor called, so any clean-up has |
577 |
|
* to be done using the restore method. |
578 |
|
*/ |
579 |
79821977 |
static void* operator new(size_t size, ContextMemoryManager* pCMM) { |
580 |
79821977 |
Trace("context_mm") << "Context::new " << size << " in " << pCMM << std::endl; |
581 |
79821977 |
return pCMM->newData(size); |
582 |
|
} |
583 |
|
|
584 |
|
/** |
585 |
|
* Corresponding placement delete. Note that this is provided just |
586 |
|
* to satisfy the requirement that a placement delete should be |
587 |
|
* provided for every placement new. It would only be called if a |
588 |
|
* ContextObj constructor throws an exception after a successful |
589 |
|
* call to the above new operator. |
590 |
|
*/ |
591 |
|
static void operator delete(void* pMem, ContextMemoryManager* pCMM) {} |
592 |
|
|
593 |
|
/** |
594 |
|
* Create a new ContextObj. The initial scope is set to the bottom |
595 |
|
* scope of the Context. Note that in the common case, the copy |
596 |
|
* constructor is called to create new ContextObj objects. The |
597 |
|
* default copy constructor does the right thing, so we do not |
598 |
|
* explicitly define it. |
599 |
|
*/ |
600 |
|
ContextObj(Context* context); |
601 |
|
|
602 |
|
/** |
603 |
|
* Create a new ContextObj. This constructor takes an argument that |
604 |
|
* specifies whether this ContextObj is itself allocated in context |
605 |
|
* memory. If it is, it's invalid below the current scope level, so |
606 |
|
* we don't put it in scope 0. |
607 |
|
* |
608 |
|
* WARNING: Read the notes above on "Gotchas when allocating |
609 |
|
* contextual objects with non-standard allocators." |
610 |
|
*/ |
611 |
|
ContextObj(bool allocatedInCMM, Context* context); |
612 |
|
|
613 |
|
/** |
614 |
|
* Destructor does nothing: subclass must explicitly call destroy() instead. |
615 |
|
*/ |
616 |
64632936 |
virtual ~ContextObj() {} |
617 |
|
|
618 |
|
/** |
619 |
|
* If you want to allocate a ContextObj object on the heap, use this |
620 |
|
* special new operator. To free this memory, instead of |
621 |
|
* "delete p", use "p->deleteSelf()". |
622 |
|
*/ |
623 |
57088487 |
static void* operator new(size_t size, bool) { |
624 |
57088487 |
return ::operator new(size); |
625 |
|
} |
626 |
|
|
627 |
|
/** |
628 |
|
* Corresponding placement delete. Note that this is provided for |
629 |
|
* the compiler in case the ContextObj constructor throws an |
630 |
|
* exception. The client can't call it. |
631 |
|
*/ |
632 |
|
static void operator delete(void* pMem, bool) { |
633 |
|
::operator delete(pMem); |
634 |
|
} |
635 |
|
|
636 |
|
/** |
637 |
|
* Use this instead of delete to delete memory allocated using the special |
638 |
|
* new function provided above that takes a bool argument. Do not call this |
639 |
|
* function on memory allocated using the new that takes a |
640 |
|
* ContextMemoryManager as an argument. |
641 |
|
*/ |
642 |
57088487 |
void deleteSelf() { |
643 |
57088487 |
Debug("context") << "deleteSelf(" << this << ") " << typeid(*this).name() << std::endl; |
644 |
57088487 |
this->~ContextObj(); |
645 |
57088487 |
::operator delete(this); |
646 |
57088487 |
} |
647 |
|
|
648 |
|
/** |
649 |
|
* Use this to enqueue calling deleteSelf() at the time of the destruction of |
650 |
|
* the enclosing Scope. |
651 |
|
*/ |
652 |
|
void enqueueToGarbageCollect(); |
653 |
|
|
654 |
|
};/* class ContextObj */ |
655 |
|
|
656 |
|
/** |
657 |
|
* For more flexible context-dependent behavior than that provided by |
658 |
|
* ContextObj, objects may implement the ContextNotifyObj interface |
659 |
|
* and simply get a notification when a pop has occurred. See |
660 |
|
* Context class for how to register a ContextNotifyObj with the |
661 |
|
* Context (you can choose to have notification come before or after |
662 |
|
* the ContextObj objects have been restored). |
663 |
|
*/ |
664 |
|
class ContextNotifyObj { |
665 |
|
|
666 |
|
/** |
667 |
|
* Context is our friend so that when the Context is deleted, any |
668 |
|
* remaining ContextNotifyObj can be removed from the Context list. |
669 |
|
*/ |
670 |
|
friend class Context; |
671 |
|
|
672 |
|
/** |
673 |
|
* Pointer to next ContextNotifyObject in this List |
674 |
|
*/ |
675 |
|
ContextNotifyObj* d_pCNOnext; |
676 |
|
|
677 |
|
/** |
678 |
|
* Pointer to previous ContextNotifyObject in this list |
679 |
|
*/ |
680 |
|
ContextNotifyObj** d_ppCNOprev; |
681 |
|
|
682 |
|
/** |
683 |
|
* Return reference to next link in ContextNotifyObj list. Used by |
684 |
|
* Context::addNotifyObj methods. |
685 |
|
*/ |
686 |
540631 |
ContextNotifyObj*& next() { return d_pCNOnext; } |
687 |
|
|
688 |
|
/** |
689 |
|
* Return reference to prev link in ContextNotifyObj list. Used by |
690 |
|
* Context::addNotifyObj methods. |
691 |
|
*/ |
692 |
540631 |
ContextNotifyObj**& prev() { return d_ppCNOprev; } |
693 |
|
|
694 |
|
protected: |
695 |
|
/** |
696 |
|
* This is the method called to notify the object of a pop. It must be |
697 |
|
* implemented by the subclass. It is protected since context is out |
698 |
|
* friend. |
699 |
|
*/ |
700 |
|
virtual void contextNotifyPop() = 0; |
701 |
|
|
702 |
|
public: |
703 |
|
|
704 |
|
/** |
705 |
|
* Constructor for ContextNotifyObj. Parameters are the context to |
706 |
|
* which this notify object will be added, and a flag which, if |
707 |
|
* true, tells the context to notify this object *before* the |
708 |
|
* ContextObj objects are restored. Otherwise, the context notifies |
709 |
|
* the object after the ContextObj objects are restored. The |
710 |
|
* default is for notification after. |
711 |
|
*/ |
712 |
|
ContextNotifyObj(Context* pContext, bool preNotify = false); |
713 |
|
|
714 |
|
/** |
715 |
|
* Destructor: removes object from list |
716 |
|
*/ |
717 |
|
virtual ~ContextNotifyObj(); |
718 |
|
|
719 |
|
};/* class ContextNotifyObj */ |
720 |
|
|
721 |
349702118 |
inline void ContextObj::makeCurrent() |
722 |
|
{ |
723 |
349702118 |
if(!(d_pScope->isCurrent())) { |
724 |
79821977 |
update(); |
725 |
|
} |
726 |
349702118 |
} |
727 |
|
|
728 |
|
inline void ContextObj::makeSaveRestorePoint() { update(); } |
729 |
|
|
730 |
144456372 |
inline void Scope::addToChain(ContextObj* pContextObj) |
731 |
|
{ |
732 |
144456372 |
if(d_pContextObjList != NULL) { |
733 |
140479457 |
d_pContextObjList->prev() = &pContextObj->next(); |
734 |
|
} |
735 |
|
|
736 |
144456372 |
pContextObj->next() = d_pContextObjList; |
737 |
144456372 |
pContextObj->prev() = &d_pContextObjList; |
738 |
144456372 |
d_pContextObjList = pContextObj; |
739 |
144456372 |
} |
740 |
|
|
741 |
|
} // namespace context |
742 |
|
} // namespace cvc5 |
743 |
|
|
744 |
|
#endif /* CVC5__CONTEXT__CONTEXT_H */ |