1 /* $Header: d:/cvsroot/tads/tads3/VMUNDO.H,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */
2 
3 /*
4  *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
5  *
6  *   Please see the accompanying license file, LICENSE.TXT, for information
7  *   on using and copying this software.
8  */
9 /*
10 Name
11   vmundo.h - VM undo manager
12 Function
13 
14 Notes
15 
16 Modified
17   10/29/98 MJRoberts  - Creation
18 */
19 
20 #ifndef VMUNDO_H
21 #define VMUNDO_H
22 
23 #include "t3std.h"
24 #include "vmtype.h"
25 
26 
27 /* ------------------------------------------------------------------------ */
28 /*
29  *   Undo record.  The VM undo manager maintains a list of these records;
30  *   each record contains the information necessary to undo one particular
31  *   data change in an object.  Each object class (TADS object, array,
32  *   etc) keeps a linked list of undo records for its own actions.
33  */
34 struct CVmUndoRecord
35 {
36     /*
37      *   Object ID.  All undoable state is stored in objects, hence each
38      *   undo record is associated with an object.
39      */
40     vm_obj_id_t obj;
41 
42     /*
43      *   Identifier - the meaning of this member is defined by the object
44      *   that created the record.  For TADS objects, this is a property ID
45      *   specifying the property that was changed; for arrays, it's the
46      *   index of the array element that changed.  Other object
47      *   metaclasses may assign different meanings.
48      */
49     union
50     {
51         /* property value key */
52         vm_prop_id_t prop;
53 
54         /* integer value key */
55         uint32 intval;
56 
57         /* pointer value key */
58         void *ptrval;
59     } id;
60 
61     /*
62      *   Data value.  This is the data value prior to the change.  In some
63      *   cases, the type may be 'empty' to indicate that the original
64      *   value did not exist; for example, when a property is added to a
65      *   TADS object and the property was not present previously, we
66      *   create an undo record with type 'empty' to indicate that the
67      *   property must be deleted on undo.
68      */
69     vm_val_t oldval;
70 };
71 
72 
73 /*
74  *   Undo meta-record.  Each slot in the undo array is one of these
75  *   meta-records, which is a union of the normal undo record and an undo
76  *   link pointer.  The first undo record in any savepoint is always a
77  *   link pointer; all of the other records are normal undo records.
78  */
79 union CVmUndoMeta
80 {
81     /* the first entry in each savepoint is a link entry */
82     struct
83     {
84         /* pointer to the first record in the previous savepoint */
85         CVmUndoMeta *prev_first;
86 
87         /* pointer to the first record in the next savepoint */
88         CVmUndoMeta *next_first;
89     } link;
90 
91     /* every entry but the first in a savepoint is an ordinary undo record */
92     CVmUndoRecord rec;
93 };
94 
95 
96 /* ------------------------------------------------------------------------ */
97 /*
98  *   Undo manager
99  */
100 class CVmUndo
101 {
102 public:
103     /*
104      *   create the undo manager, specifying the upper limit for memory
105      *   usage and retained savepoints
106      */
107     CVmUndo(size_t undo_record_cnt, uint max_savepts);
108 
109     /* delete the undo manager */
110     ~CVmUndo();
111 
112     /*
113      *   Create a savepoint.  If doing so would exceed the maximum number
114      *   of savepoints, we'll discard the oldest savepoint.
115      */
116     void create_savept(VMG0_);
117 
118     /* get the current savepoint ID */
get_cur_savept()119     vm_savept_t get_cur_savept() const { return cur_savept_; }
120 
121     /* determine how many savepoints exist */
get_savept_cnt()122     uint get_savept_cnt() const { return savept_cnt_; }
123 
124     /* drop all undo information */
125     void drop_undo(VMG0_);
126 
127     /*
128      *   Allocate and initialize an undo record with a property key or
129      *   with an integer key.
130      *
131      *   We check that the undo manager has an active savepoint.  It may
132      *   not have an active savepoint if no savepoint has been created,
133      *   all undo records have been applied, or we've run out of space in
134      *   the current savepoint; in any of these cases, since there's no
135      *   savepoint, we don't need to keep any undo records.
136      */
137     void add_new_record_prop_key(VMG_ vm_obj_id_t obj, vm_prop_id_t key,
138                                  const vm_val_t *val);
139     void add_new_record_int_key(VMG_ vm_obj_id_t obj, uint32 key,
140                                 const vm_val_t *val);
141 
142     /*
143      *   Add a new undo record with a pointer key; returns true if the
144      *   record was created, false if not.  (This version of
145      *   add_new_record_xxx_key returns an indication of success because
146      *   the pointer value might have been allocated, in which case the
147      *   caller must deallocate the value if we didn't add an undo
148      *   record.)
149      */
150     int add_new_record_ptr_key(VMG_ vm_obj_id_t obj, void *key,
151                                const vm_val_t *val);
152 
153     /*
154      *   Apply undo to the latest savepoint.  After applying the undo
155      *   records, we delete all undo records to the savepoint; this leaves
156      *   the previous savepoint as the new most recent savepoint, so
157      *   repeating this will undo to the previous savepoint.
158      */
159     void undo_to_savept(VMG0_);
160 
161     /*
162      *   Garbage collection: mark referenced objects.  This goes through
163      *   all of the undo records; for each undo record, we have the object
164      *   that owns the undo record mark as referenced any object to which
165      *   the record refers.
166      */
167     void gc_mark_refs(VMG0_);
168 
169     /*
170      *   Garbage collection: delete obsolete weak references.  This goes
171      *   through all of the undo records; for each undo record, if the
172      *   object that owns the record is unreachable, we'll delete the undo
173      *   record; otherwise, we'll tell the object that owns the record to
174      *   clean up the record if it contains an unreachable weak reference.
175      */
176     void gc_remove_stale_weak_refs(VMG0_);
177 
178 private:
179     /*
180      *   Allocate an undo record.  Returns null if no records are
181      *   available, in which case the caller should simply skip saving
182      *   undo.
183      */
184     CVmUndoMeta *alloc_rec(VMG0_);
185 
186     /*
187      *   increment a record pointer, wrapping back at the end of the array
188      *   to the first record
189      */
inc_rec_ptr(CVmUndoMeta ** rec)190     void inc_rec_ptr(CVmUndoMeta **rec)
191     {
192         /* increment the record pointer */
193         ++(*rec);
194 
195         /* if it's at the end of the array, wrap back to the start */
196         if (*rec == rec_arr_ + rec_arr_size_)
197             *rec = rec_arr_;
198     }
199 
200     /*
201      *   Add a new record and return the new record.  If we don't have an
202      *   active savepoint, this will return null, since there's no need to
203      *   keep undo.
204      */
205     CVmUndoRecord *add_new_record(VMG_ vm_obj_id_t controlling_obj);
206 
207     /* discard the oldest savepoint */
208     void drop_oldest_savept(VMG0_);
209 
210     /* current savepoint ID */
211     vm_savept_t cur_savept_;
212 
213     /*
214      *   Number of active savepoint ID's.  If this is zero, it means that
215      *   we have no undo information and we're not keeping any undo
216      *   information.  If this is one, it means that we only have the
217      *   current savepoint stored.
218      */
219     uint savept_cnt_;
220 
221     /*
222      *   The nominal maximum number of savepoints to keep.  Any time that
223      *   we attempt to create a new savepoint, and doing so would exceed
224      *   this number of stored savepoints, we will discard the oldest
225      *   savepoint.  Note that we may not always store this many
226      *   savepoints, since we will also discard old savepoints when we run
227      *   out of available undo records attempting to create a new
228      *   savepoint.
229      */
230     vm_savept_t max_savepts_;
231 
232     /*
233      *   Pointer to the first record in the current savepoint.  This isn't
234      *   a real undo record -- it's a link record.
235      */
236     CVmUndoMeta *cur_first_;
237 
238     /*
239      *   Pointer to the first undo record in the oldest savepoint.  This
240      *   is a link record.
241      */
242     CVmUndoMeta *oldest_first_;
243 
244     /* pointer to the next free undo record */
245     CVmUndoMeta *next_free_;
246 
247     /*
248      *   Master array of undo records, and the number of records in this
249      *   list.  We pre-allocate a fixed array of these records.  If we run
250      *   out, we start discarding old undo.
251      */
252     CVmUndoMeta *rec_arr_;
253     size_t rec_arr_size_;
254 };
255 
256 
257 #endif /* VMUNDO_H */
258 
259