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